2012-05-04 5 views
3

Я играл с FunctionalDependencies-Extension Haskell вместе с MultiParamTypeClasses. Я определил следующее:Неоднозначные переменные типа с функциональными зависимостями Haskell

class Add a b c | a b -> c where 
    (~+) :: a -> b -> c 
    (~-) :: a -> b -> c 
    neg :: a -> a 
    zero :: a 

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

Когда я пытаюсь определить реализацию по умолчанию для нег или (~ -) как так:

class Add ... 
    ... 
    neg n = zero ~- n 

GHCI (7.0.4) говорит мне следующее:

Ambiguous type variables `a0', `b0', `c0' in the constraint: 
    (Add a0 b0 c0) arising from a use of `zero' 
Probable fix: add a type signature that fixes these type variable(s) 
In the first argument of `(~-)', namely `zero' 
In the expression: zero ~- n 
In an equation for `neg': neg n = zero ~- n 

Ambiguous type variable `a0' in the constraint: 
    (Add a0 a a) arising from a use of `~-' 
Probable fix: add a type signature that fixes these type variable(s) 
In the expression: zero ~- n 
In an equation for `neg': neg n = zero ~- n 

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

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

neg n = (zero :: Add a b c) ~- n 

Я думаю a, b и c здесь не в abc образуют окружающий класс, но любые ab и c, так как я могу выразить тип, который является ссылкой на переменные локального типа?

ответ

6

Прицепные neg и zero из в суперкласса, который использует только один тип:

class Zero a where 
    neg :: a -> a 
    zero :: a 

class Zero a => Add a b c | a b -> c where 
    (~+) :: a -> b -> c 
    (~-) :: a -> b -> c 

Дело в том, что ваш путь, zero :: Int может быть zero от Add Int Int Int, илиzero от Add Int Double Double и нет никакого способа устранить двусмысленность между ними, независимо от того, ссылаетесь ли вы на него изнутри реализации по умолчанию или объявления экземпляра или обычного кода.

(Вы можете возразить, что zero от Add Int Int Int и zero от Add Int Double Double будет иметь такое же значение, но компилятор не может знать, что кто-то не будет определять Add Int Char Bool в другом модуле и дать zero другое значение там.)

Разделив класс на две части, мы устраним двусмысленность.

3

Вы не можете выразить функцию zero как часть класса Add. Все переменные типа в объявлении класса должны встречаться в объявлении типа для каждой функции класса; в противном случае Haskell не сможет решить, какой тип экземпляра использовать, поскольку ему задано слишком мало ограничений.

Иными словами, zero не является собственностью выбранного вами класса.Вы в основном говорите: «Для любых трех типов a, b, c должно существовать нулевое значение для типа a», что не имеет смысла; вы можете выбрать любые b и c, и это решит проблему, поэтому b и c полностью непригодны для использования, поэтому, если у вас есть Add Int Int Int или Add Int (Maybe String) Boat, Haskell не знает, какой экземпляр предпочтительнее. Вы должны отделить свойство отрицания и «zeroness» в отдельный класс (ов) типов:

class Invertible a where 
    invert :: a -> a 

neg :: Invertible a => a -> a 
neg = invert 

class Zero a where 
    zero :: a 

class Add a b c | a b -> c where 
    (~+) :: a -> b -> c 
    (~-) :: a -> b -> c 

Я не понимаю, почему вы бы тогда даже нужны Invertible и Zero ограничений в Add; вы всегда можете добавлять числа, не зная их нулевого значения, не так ли? Зачем выражать neg как требование для ~+; есть некоторые числа, которые должны быть добавлены без их отрицания (например, натуральные числа)? Просто соблюдайте класс.

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