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

person Snowball    schedule 05.12.2013    source источник


Ответы (2)


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

g OTOH имеет тип ранга 2. Требуется, чтобы его аргумент имел полиморфный тип forall a . a. () такого типа нет, он мономорфен. Но undefined имеет этот тип (фактически только undefined и error и т. Д.), Если мы снова добавим явный 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 :: Double
4 :: Int

но я не могу

GHCi> putStrLn $ h (4 :: Integer)

‹‌interactive>: 4: 15:
Не удалось вывести (a ~ Integer)
из контекста (Показать a, Num a) < br> связанный типом, ожидаемым контекстом: (Показать a, Num a) => a
at ‹‌interactive>: 4: 12-27
` a '- переменная жесткого типа, связанная с
тип, ожидаемый контекстом: (Show a, Num a) => a
at ‹‌interactive>: 4: 12
В первом аргументе` h ', а именно `(4 :: Integer)' < br> Во втором аргументе `($) ', а именно` h (4 :: Integer)'
В выражении: putStrLn $ h (4 :: Integer)

person leftaroundabout    schedule 05.12.2013
comment
Вы говорите, что () является мономорфизмом (в какой категории?) Или вы говорите, что () мономорфен какому-то другому (неуказанному) объекту? Или вы используете термин «мономорфный» как-то иначе? - person Snowball; 05.12.2013
comment
@Snowball: я использую этот термин так, как он обычно используется в контексте Haskell. : что-то не полиморфное. Собственно, никогда не задумывался, как это связано с мономорфизмами ... - person leftaroundabout; 05.12.2013
comment
Я не думаю, что есть какая-либо связь с концепцией мономорфизма в теории категорий. - person Tom Ellis; 05.12.2013

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

f :: a -> Int
f _ = 1

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

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

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

() :: ()
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, ваша программа немедленно остановится с ошибкой. К счастью, вы не пытаетесь это оценить.

person Aadit M Shah    schedule 05.12.2013
comment
TL; DR forall's можно рассматривать как лямбды на уровне типа, а \a -> M ~> N не то же самое, что (\a -> M) ~> N. - person Daniel Gratzer; 05.12.2013
comment
цитата ›Вызов лямбда-выражения на уровне типа является неточным, см. stackoverflow.com/a/10063020/1338198. - person Zane XY; 26.04.2014