Как Haskell узнает, какой экземпляр класса типов вы имеете в виду?

Этот вопрос возник при чтении новой главы отличного Learn You a Haskell, посвященной аппликативным функторам.

Класс типов Applicative включает в себя как часть определения экземпляра Maybe:

pure = Just

Если я просто перейду в GHCi и импортирую Control.Applicative, и сделаю:

pure (3+)

Я ничего не понимаю (имеет смысл). Но если я использую его в части выражения:

pure (3+) <*> Just 4

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

Если мое замешательство имеет смысл, может ли кто-нибудь подробно объяснить, что происходит?


person J Cooper    schedule 03.05.2009    source источник


Ответы (4)


Это просто вывод типа. Оператор (<*>) требует, чтобы оба аргумента использовали один и тот же экземпляр Applicative. Правая сторона - это Maybe, поэтому левая сторона также должна быть Maybe. Вот как он определяет, какой экземпляр здесь используется. Вы можете посмотреть тип любого выражения в интерпретаторе, набрав :t expression, и, возможно, если вы просто пройдетесь по каждому подвыражению и посмотрите на выведенный тип, вы получите лучшее представление о том, что происходит.

person newacct    schedule 03.05.2009
comment
А, значит, компилятор делает что-то вроде Хмм, тип этого аргумента неоднозначен, поэтому позвольте мне проверить тип следующего аргумента и вернуться к нему? - person J Cooper; 03.05.2009
comment
Вывод типа довольно сложен, но стоит знать, что это не происходит за один шаг. Средство вывода типа обычно собирает некоторую информацию за один шаг, а на более позднем этапе - дополнительную информацию. Таким образом, он не просто выводит правильные типы для всего за один раз, слева направо. В этом случае он сделает вывод, что pure относится к типу (Applicative a1) => a1 (Int -> Int), где a1 - это просто переменная придуманного типа, на более позднем этапе во время вывода типа он придет к выводу, что a1 должен быть Maybe, а затем он будет везде заменять Maybe на a1. - person Tom Lokhorst; 04.05.2009
comment
Спасибо, это очень полезно! - person J Cooper; 04.05.2009

Стоит взглянуть на тип, который компилятор выводит для pure (3+):

Prelude Control.Applicative> :t pure (3+)
pure (3+) :: (Num a, Applicative f) => f (a -> a)

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

*Showfun Control.Applicative> pure (3+) :: Maybe (Double -> Double)
Just <function>

(Это работает, потому что Showfun имеет объявление экземпляра, которое выводит значение функции как <function>.)

Вопрос лишь в том, когда компилятор накопит достаточно информации, чтобы принять решение.

person Norman Ramsey    schedule 03.05.2009
comment
Из любопытства, находится ли модуль Showfun в каком-то существующем пакете или это вы написали сами? Реализация достаточно проста: instance Show (a -> b) where; show _ = "<function>" - person Tom Lokhorst; 04.05.2009
comment
Я сам написал Showfun, чтобы напечатать пример pure (3+) с помощью Just. - person Norman Ramsey; 05.05.2009
comment
Я полагаю, ShowFunctions теперь находится в QuickCheck. - person porges; 26.08.2009

Чтобы немного расширить ответ newacct, если информации недостаточно для вывода фактического типа, компилятор может (в некоторых случаях) попытаться выбрать тип по умолчанию, ограниченный теми, которые удовлетворяли бы рассматриваемым ограничениям типа. В этом случае предполагаемый тип - IO (n -> n) для некоторого трудно определяемого экземпляра Num => n. Затем GHCi оценивает его и отбрасывает возвращаемое значение без видимого эффекта.

person bdonlan    schedule 03.05.2009

Вот интересный поток SO по выводу типа. Не специфично для Haskell, но есть много хороших ссылок и прочего, чтобы прочитать о выводе типов на функциональных языках.

person JP Alioto    schedule 03.05.2009