RankNTypes и сфера действия `forall'

В чем разница между этими?

{-# LANGUAGE RankNTypes #-}

f :: forall a. a -> Int
f _ = 1

g :: (forall a. a) -> Int
g _ = 1

В частности, почему я получаю ошибку с g ()?

ghci> f ()
1
ghci> g ()
<interactive>:133:3:
    Couldn't match expected type `a' with actual type `()'
      `a' is a rigid type variable bound by
          a type expected by the context: a at <interactive>:133:1
    In the first argument of `g', namely `()'
    In the expression: g ()
    In an equation for `it': it = g ()

ghci> f undefined
1
ghci> g undefined
1

2 ответа

Решение

f это просто обычная полиморфная функция Haskell98, кроме forall выписан явно. Таким образом, все переменные типа в сигнатуре являются параметрами, которые выбирает вызывающая сторона (без каких-либо ограничений); в вашем случае это решено a ~ (),

g OTOH имеет тип ранга 2. Это требует, чтобы его аргумент имел полиморфный тип forall a . a, () не имеет такого типа, он мономорфен. Но undefined имеет этот тип (на самом деле, только неопределенный, ошибка и т. д.), если мы добавим явное forall снова.

Возможно, это станет яснее с менее тривиальной функцией Rank2:

h :: (forall a . (Show a, Num a) => a) -> String
h a = show a1 ++ " :: Double\n"
     ++ show a2 ++ " :: Int"
 where a1 :: Double; a2 :: Int
       a1 = a; a2 = a

GHCi> putStrLn $ h 4
4.0:: Двухместный
4:: Int

но я не могу сделать

GHCi> putStrLn $ h (4:: Целое число)

<Интерактивными>: 4: 15:
Не удалось вывести (целое число)
из контекста (Показать, Num a)
связан с типом, ожидаемым контекстом: (Показать a, Num a) => a
в <интерактивное>:4:12-27
"a" - это переменная жесткого типа, связанная
тип, ожидаемый контекстом: (Показать a, Num a) => a
в <интерактивное>: 4: 12
В первом аргументе `h', а именно`(4:: Integer)'
Во втором аргументе `($)', а именно: h (4:: Integer)
В выражении: putStrLn $ h (4:: Integer)

Думать о forall как функция анонимного типа. Все типы данных в Haskell, которые имеют переменные типа в сигнатуре типа, неявно имеют forall, Например, рассмотрим:

f :: a -> Int
f _ = 1

Выше функция f принимает аргумент любого типа и возвращает Int, Где a родом из? Это исходит от forall квантор. Следовательно это эквивалентно:

f :: (forall a . a -> Int)
f _ = 1

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

() :: ()
10 :: Int
pi :: Floating a => a

Вот () а также 10 являются мономорфными (то есть они могут быть только одного конкретного типа). С другой стороны pi является полиморфным с ограничением класса типов (т. е. он может быть любого типа, если этот тип является экземпляром Floating). Тип pi выписано явно:

pi :: (forall a . Floating a => a)

Опять forall квантификатор действует как функция типа. Он предоставляет вам переменную типа a, Теперь рассмотрим тип функции g:

g :: (forall a . a) -> Int
g _ = 1

Вот g ожидает аргумент типа forall a . a и возвращает Int, Это причина g () не работает: () имеет тип ()не тип forall a . a, На самом деле единственное значение типа forall a . a является undefined:

undefined :: a

Выписано явно с forall:

undefined :: (forall a . a)

Если вы заметили, я всегда ставил круглые скобки вокруг forall количественные. Я сделал это, чтобы показать вам, что когда вы используете forall квантификация по функции квантификация простирается полностью вправо. Это похоже на лямбду: если вы не поставите скобки вокруг лямбды, Haskell расширит лямбда-функцию до упора вправо. Отсюда тип f является (forall a . a -> Int) и не (forall a . a) -> Int,

Помните, что в первом случае Haskell ожидает, что тип аргумента будет a (то есть что угодно). Однако во втором случае Haskell ожидает, что тип аргумента будет (forall a . a) (т.е. undefined). Конечно, если вы попытаетесь оценить undefined тогда ваша программа немедленно остановится с ошибкой. К счастью, вы не пытаетесь это оценить.

Другие вопросы по тегам