2015-09-03 3 views
1

Здесь я определил два класса: F и O. F предназначен для полиморфной функции f и O для полиморфного значения o.Почему это выражение неоднозначно?

{-# LANGUAGE MultiParamTypeClasses #-} 

class F a b where 
    f :: a -> b 

class O a where 
    o :: a 

data K = K 
data L = L 
data M = M 

instance O K where 
    o = K 

instance O L where 
    o = L 

instance F K L where 
    f K = L 

instance F L K where 
    f L = K 

instance F L L where 
    f L = L 

instance F M K where 
    f M = K 

Чтобы сделать это легко, вот список возможных случаев для любого класса:

F K L 
F L K 
F L L 
F M K 

O K 
O L 

Теперь, если я ввожу следующее в GHCi:

f o :: K 

Я ожидаю, что Haskell что конечный результат должен быть K, это означает, что возможные случаи для f: F M K и F L K, но с o может быть только L, но не M, это должно быть так, что o является L и f является F L K. То есть без какой-либо другой возможности результат должен быть K. Однако, оказывается, Haskell не может понять это:

*Main> f o :: K 

<interactive>:2:1: 
    No instance for (F a0 K) arising from a use of `f' 
    The type variable `a0' is ambiguous 
    Possible fix: add a type signature that fixes these type variable(s) 
    Note: there are several potential instances: 
     instance F M K -- Defined at hw.hs:28:10 
     instance F L K -- Defined at hw.hs:22:10 
    Possible fix: add an instance declaration for (F a0 K) 
    In the expression: f o :: K 
    In an equation for `it': it = f o :: K 

<interactive>:2:3: 
    No instance for (O a0) arising from a use of `o' 
    The type variable `a0' is ambiguous 
    Possible fix: add a type signature that fixes these type variable(s) 
    Note: there are several potential instances: 
     instance O L -- Defined at hw.hs:16:10 
     instance O K -- Defined at hw.hs:13:10 
    In the first argument of `f', namely `o' 
    In the expression: f o :: K 
    In an equation for `it': it = f o :: K 

Как вы думаете, это потому, что компилятор не достаточно умный, или вы думаете, что есть реальная амбивалентность в моем выражении?

+8

Этого слишком много, чтобы спросить. Помните, что классы типов * open *, что означает, что код должен продолжать компилироваться при добавлении новых экземпляров. Мы могли бы легко добавить экземпляр для 'O M', что сделало бы ваше выражение неоднозначным, и, следовательно, оно должно быть уже неоднозначным в открытом мире. Однако новые [закрытые типы семейств] (https://wiki.haskell.org/GHC/Type_families#Closed_family_simplification) могут делать то, что вы хотите - я не работал с ними много, поэтому я не могу быть уверен. – luqui

+0

И на самом деле (используя 'FlexibleInstances') вы можете просто определить' x :: O M => K; x = f (o :: M) ', а затем используйте' x' в другом модуле, который имеет экземпляр O M. Поэтому в этом смысле выбор типа 'o' в выражении' f o' действительно неоднозначен. –

+0

@ReidBarton, вы имеете в виду 'FlexibleContexts'? – dfeuer

ответ

1

o возвращение любого типа: o :: a.

подачи ее f дает

:t f o 
    f o :: F a b => b 

Это означает, что a может быть любого типа здесь. Таким образом, o неоднозначен.

f o :: K состояния b быть K в f :: a -> b в F a b.

обоих случаях выше, дает возможный экземпляр для f

instance F a0 K 

который имеет два кандидата

instance F L K 

instance K M K 

o возвращает любой экземпляр класса O. Есть только два экземпляра, определенные:

instance O K 
instance O L 

Так f o :: K является неоднозначным, потому o неоднозначно.


Я думаю GHC выводит типы таким образом (поиск только для подходящих случаев) для того, чтобы не застрять в цикле. Но я только начал изучать его, поэтому я могу ошибаться.

Есть методы, описанные here и here (shortly), которые помогают компилятору вывести правильные экземпляры.

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