2013-07-28 3 views
3

Можно ли написать функцию в Haskell, который возвращает общий тип, когда:Generic функция результат в Haskell на основе некоторых предикатов

  • в теле этой функции мы выводим в результате 2 отдельные виды A и B (на основе некоторых вычислений)
  • типы A и B имеют общий type classC

Позволяет увидеть пример кода. Контроллер типа ДОЛЖЕН быть в состоянии проверить, что этот код верен - функция test выдает экземпляр типа A или B, поэтому мы можем выполнить f на результат.

data A = A 
data B = B 

class C a where 
    f :: a -> Int 

instance C A where 
    f x = 2 

instance C B where 
    f x = 3 

-- This function fails to compile: 
-- I want something like: 
-- test :: C a => Int -> a 
test x = if x < 1 
     then A 
     else B 

main = do 
    print $ f $ test 0 
    print $ f $ test 1 

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

+0

'текст :: С а => Int -> a' означает, что * вызывающий * получает выбрать тип' '- универсально количественно. Если вызываемый абонент выбирает, вам нужен экзистенциальный: 'data SomeC, где SomeC :: C a => a -> SomeC'. – luqui

ответ

3

Проблема в том, что переменные типа Haskell универсально оцениваются.

Это означает, что они могут быть прочитаны как

forall a. C a => Int -> a 

Это означает, что абонент выбирает конкретный тип a. Не вызывающий.

Чтобы исправить это, вам понадобится нечто, называемое «экзистенциальными» переменными. Универсально квантифицированные переменные следует читать как «forall», экзистенциально квантифицированные переменные следует читать как «существует».

Быстрый набросок того, как сделать это

{-# LANGUAGE ExistentialQuantification, GADTs #-} 

-- Use existentials 
data Box = forall a. C a => Box a 

-- Use GADTs 
data Box' where 
    Box' :: C a => a -> Box' 

instance C Box where 
    f (Box a) = f a 

test :: Int -> Box 
test x = if x < 1 
    then Box A 
    else Box B 

Суть в том, что мы прячем наш конкретный экземпляр C позади этого типа Box данных, а затем мы переходим, что вокруг вместо переменного универсально количественного типа.

В качестве альтернативы, вы можете избежать типа Box для Rank2Types с простыми продолжениями.

test :: Int -> (forall a. C a => a -> r) -> r 
test x c = if x < 1 then c A else c B 
main = do 
    test 1 (print . f) 
    test 0 (print . f) 

Fun причуда экзистенциальных переменных, попробуйте этот код с GHC

fun = foo 
    where (Box foo) = test 1 
+0

Открытый вопрос, можно ли это сделать с помощью 'Cont', используя непроизводительные типы? – jozefg

+0

Вы имели в виду сказать «универсально квантифицированный», а не «универсально квалифицированный»? –

+0

@johanatan Да, спасибо – jozefg

Смежные вопросы