1

Допустим, у меня есть следующий (глупый) класс:Унификация Weirdness в класс типов Instance

class BlindMap m where 
    mapB :: m a -> m b 

я мог бы предоставить следующий [] экземпляр:

instance BlindMap [] where 
    mapB = map id 

Тип ОРЗ является [a] -> [a], который должен унифицировать с [a] -> [b], но GHC так не считает:

Couldn't match type ‘a’ with ‘b’ 
    ‘a’ is a rigid type variable bound by 
     the type signature for mapB :: [a] -> [b] at dingas.hs:11:5 
    ‘b’ is a rigid type variable bound by 
     the type signature for mapB :: [a] -> [b] at dingas.hs:11:5 
Expected type: a -> b 
    Actual type: b -> b 
Relevant bindings include 
    mapB :: [a] -> [b] 
In the first argument of ‘map’, namely ‘id’ 
In the expression: map id 

Что мне здесь не хватает?

Заранее спасибо.

+0

Да, вы правы! – josejuan

+3

Короче говоря, '[a] -> [a]' и '[a] -> [b]' действительно объединяются; но объединение здесь не представляет интереса. Вместо этого интерес представляет проверка типа; «может ли этот термин получить этот тип?». 'map id' не может быть задан тип' [a] -> [b] '- вы считаете это, или это также является частью путаницы? –

+0

Да, я не понял, что типы реализации метода должны быть _exactly_ равными типу, указанному классом, а не просто унифицированным. – TWhit

ответ

2

Что мне здесь не хватает?

map id создает список значений какого-либо произвольного типа с учетом списка таких значений. [a] -> [b] обещает создать список значений некоторого произвольного типа, учитывая список значений потенциально различного типа.

Следовательно, то, что он ожидает, это a -> b, но ваша функция, основанная на идентификаторе, может принимать только то, что она возвращает, поэтому b -> b.

+0

_Potentially_ другой, а не обязательно. Специфическим типом 'mapB' в объявлении экземпляра является' [a] -> [a] '. Пусть 'm ~ []', так как это тело экземпляра, в котором мы находимся, '[a] -> [a]' является унифицированным с '[a] -> [b]', позволяя 'a ~ b'. Другие примеры включают 'map :: (a -> b) -> [a] -> [b]'; 'map id :: [a] -> [a]' и в GHCi ': t [undefined :: a -> b, undefined a -> a]' yields '[undefined :: a -> b, undefined a -> a] :: [b -> b] '. – TWhit

+1

@TWhit вы смешиваете два примера. В вашем случае функция 'id' делает тип отображения более конкретным, но это потому, что мы * вне * функции, и мы используем * одну из возможных конкретизации *. Внутренняя часть этого еще должна быть реализована таким образом, чтобы «a» и «b» отличались друг от друга. То, что вы делаете, - это реализация вашей функции, а не ее использование, поэтому написанное выше неверно, поскольку кто-то может захотеть использовать ваш класс в контексте, где 'a ~/~ b'. –

+1

Хорошо, я вижу; в экземпляре класса мне не разрешено конкретизировать тип, указанный в определении класса. – TWhit

0

В целом, вы сказали, что обеспечит функции с типом:

forall a b.[a] -> [b] 

в определениях экземпляра. Компилятор взял ваше слово.

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

Перед тем, как продолжить, вы должны попытаться написать функцию

forall a b. a -> b 

, и вы увидите, что вы в лучшем случае можно придумать что-то вроде:

f x = f x 
g x = error "not terminating." 

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

instance BlindMap (Maybe a) where mapB _ = Nothing 
instance BlindMap ([a])  where mapB _ = [] 

и так и в том же духе.

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