Предоставить другое тело функции для универсальной функции в зависимости от типа - PullRequest
5 голосов
/ 12 декабря 2011

Предположим, у меня есть какая-то общая функция

genericFunc :: a -> b
genericFunc x = doSomeHardWork

Но для конкретного типа есть гораздо более эффективный способ, чем можно было бы сделать genericFunc.

genericFunc :: ParticularType -> b
genericFunc x = doSomeEasyWork

Что такоелучший способ объединить эти два функциональных тела в один и тот же genericFunc, так что при использовании на ParticularType он будет doSomeEasyWork, но при использовании на других типах он будет doSomeHardWork?Я специально исключаю возможность использования другого имени или других модулей.

Я считаю, что это можно сделать с помощью класса типов, но меня больше интересуют решения, использующие языковые прагмы.У меня есть смутные предположения, что это можно сделать с помощью языковых прагм, но я понятия не имею, как.Бонусные баллы, если вы сравните и сопоставите эти подходы и / или любые другие возможные подходы.

Ответы [ 2 ]

8 голосов
/ 12 декабря 2011

Это можно сделать с помощью классов типов, определив универсальный метод в определении класса и переопределив его в экземпляре.Всегда будет использоваться переопределенная функция.

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
2 голосов
/ 12 декабря 2011

С GHC вы можете использовать RULES прагму .

{-# RULES "genericFunc/easy" genericFunc = doSomeEasyWork #-}

Это будет применять правило перезаписи всякий раз, когда типы совпадают, и использовать универсальную реализацию в противном случае.

...