Это можно сделать с помощью классов типов, определив универсальный метод в определении класса и переопределив его в экземпляре.Всегда будет использоваться переопределенная функция.
class ContainsInt c where
toList :: c -> [Int]
-- generic function
elem :: Int -> c -> Bool
elem n x = Prelude.elem n (toList x)
instance ContainsInt () where
toList _ = []
-- Override the generic function for type ()
elem _ _ = False
Альтернативой, поддерживаемой GHC, является использование правила перезаписи.Правило перезаписи говорит GHC заменять одно выражение другим, когда это возможно.Если замена напечатана неправильно, это не будет сделано, так что вы можете использовать это для замены функции на специализированную версию.Правило перезаписи дается прагмой {-# RULES #-}
.
class ContainsInt c where
toList :: c -> [Int]
elem :: ContainsInt c => Int -> c -> Bool
elem n x = Prelude.elem n (toList x)
-- Replace 'elem' by 'elemUnit' if it has the same type
{-# RULES "elem()" forall. elem = elemUnit #-}
elemUnit :: Int -> () -> Bool
elemUnit _ _ = False
Правила перезаписи выполняются по усмотрению компилятора, поэтому специализированная функция может вызываться или не вызываться в любой конкретной ситуации.Например, перезапись может зависеть от того, решит ли компилятор встроить функцию:
foo :: ContainsInt c -> Int -> [c] -> [Bool]
-- Must use the generic function
foo n cs = map (elem n) cs
useFoo :: Int -> [()] -> [Bool]
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'.
-- Otherwise rewriting cannot happen.
useFoo n cs = foo n cs