Неявные параметры Haskell и полиморфная рекурсия
У меня есть вопрос по главе " Неявные параметры и полиморфная рекурсия " Руководства пользователя GHC.
Код
len1 :: [a] -> Int
len1 xs = let ?acc = 0 in len_acc1 xs
len_acc1 [] = ?acc
len_acc1 (_:xs) = let ?acc = ?acc + (1::Int) in len_acc1 xs
------------
len2 :: [a] -> Int
len2 xs = let ?acc = 0 in len_acc2 xs
len_acc2 :: (?acc :: Int) => [a] -> Int
len_acc2 [] = ?acc
len_acc2 (_:xs) = let ?acc = ?acc + (1::Int) in len_acc2 xs
В главе говорится
В первом случае len_acc1 мономорфен в своей правой части, поэтому неявный параметр ?acc не передается в рекурсивный вызов. В последнем случае, поскольку len_acc2 имеет сигнатуру типа, рекурсивный вызов выполняется для полиморфной версии, которая принимает ?acc в качестве неявного параметра.
Вопросы
Означает ли в данном случае «мономорфный в своей правой части» тип
len_acc1 :: (?acc :: Int) => [a] -> p
? Почему ghci говоритlen_acc1 :: (?acc::Int) => [a] -> Int
?Почему первая функция мономорфна, а вторая нет? Если бы мое понимание было правильным, было бы наоборот.
Или, может быть, это означает, что тип
len_acc1 :: [a] -> Int
, но каждый случай относится к?acc
неявное значение, поэтому я считаю, что тип должен упоминать(?acc :: Int)
ограничение.Как этот мономорфизм подразумевает, что неявный параметр не передается в функцию?
2 ответа
В Haskell мы часто называем сигнатуры типов с переменными типа полиморфными, например полиморфную функцию
id :: a -> a
Однако эта сигнатура является полиморфной не потому, что она включает переменную типа, а потому, что эта переменная определяется количественно . В Haskell сигнатуры типов неявно универсальны, поэтому приведенное выше эквивалентно:
id :: forall a. a -> a
который на самом деле является принятым синтаксисом, если вы включите
Когда Haskell печатает привязки без подписи типа, он использует алгоритм типизации Хиндли-Милнера для назначения типов. Этот алгоритм работает, назначая сигнатуры мономорфных типов наборам взаимозависимых привязок, а затем уточняя их, определяя отношения между ними. Этим сигнатурам разрешено содержать переменные типа (!), но они по-прежнему считаются мономорфными, поскольку переменные не определяются количественно. То есть предполагается, что переменные обозначают определенные типы, просто мы еще не выяснили их, потому что находимся в середине проверки типов.
После завершения проверки типов и присвоения «окончательных» мономорфных типов выполняется отдельный шаг обобщения. Все переменные типа, которые остаются в мономорфных типах, которые не были определены как фиксированные типы, такие как
Документация ссылается на тот факт, что при выводе типа алгоритм присваивает ему мономорфный тип, в конечном итоге окончательный мономорфный тип
Напротив, когда предоставляется полиморфная подпись верхнего уровня:
len_acc2 :: forall a. (?acc :: Int) => [a] -> Int
привязка является полиморфной (вместе со связанным с ней ограничением) везде, где она используется, в частности, в рекурсивном вызове.
Результат в том, что
Скорее всего, вы столкнетесь с такой ситуацией, как следующая, когда требуется «очевидная» подпись типа, потому что мономорфный тип не может быть выведен:
data Binary a = Leaf a | Pair (Binary (a,a)) deriving (Show)
-- following type signature is required...
-- toList :: Binary a -> [a]
toList (Leaf x) = [x]
toList (Pair b) = concatMap (\(x,y) -> [x,y]) (toList b)
main = print $ toList (Pair (Pair (Leaf ((1,2),(3,4)))))
Я могу ответить на вопрос лишь частично.
Почему ghci говорит
len_acc1 :: (?acc::Int) => [a] -> Int
?
Это потому, что первое предложение возвращает
?acc
и можно предположить, что из
?acc + (1::Int)
во втором пункте.
"в... правой стороне"
означает часть после
=
, который имеет тип
Int
как и выше, который является мономорфным.
len_acc1 :: (?acc :: Int) => [a] -> p
был бы полиморфным .
Или, может быть, это означает, что тип len_acc1 :: [a] -> Int, но каждый случай относится к неявному значению ?acc, поэтому я считаю, что тип должен упоминать ограничение (?acc :: Int).
Вот что он, кажется, говорит мне; что, по крайней мере, временно он рассматривается как имеющий этот тип. Но тогда я не понимаю, почему он вообще компилируется.
Я пытался прочитать статью, где были определены неявные параметры, но не нашел ответа.