«Действительно» универсальная функция в Haskell - PullRequest
14 голосов
/ 19 августа 2011

Предположим, у меня составной тип данных -

data M o = M (String,o)

Теперь я могу определить функцию, которая работает для ВСЕХ M независимо от o. Например -

f :: M o -> M o
f (M (s,o)) = M (s++"!", o)

Тем не менее, f не так широко, как хотелось бы. В частности, использование f в выражении фиксирует тип o, поэтому я не могу снова использовать f с другим типом o. Например, следующее не проверяет тип -

p f = undefined where
  m1 = M ("1", ())
  m2 = M ("2", True)
  m1' = f m1
  m2' = f m2

выдает ошибку - Couldn't match expected type 'Bool' with actual type '()'

Удивительно, но если я не предоставлю f в качестве аргумента и вместо этого просто использую глобальное определение f, тогда он компилируется и работает отлично! то есть это компилируется -

p = undefined where
  m1 = M ("1", ())
  m2 = M ("2", True)
  m1' = f m1
  m2' = f m2

Есть ли для этого особая причина? Как я могу обойти эту проблему, то есть определить функцию f, которая может быть применена ко всем (M o), даже если o изменяется в пределах одного и того же выражения? Я предполагаю, что в игру вступают экзистенциальные типы, но я просто не могу понять, как.

Ответы [ 3 ]

15 голосов
/ 19 августа 2011

Проблема в том, что компилятор не может правильно определить тип p.Вам нужно будет указать тип подписи:

p :: (forall o. M o -> M o) -> a

Это тип ранга 2, поэтому вам потребуется расширение языка:

{-# LANGUAGE Rank2Types #-}

или

{-# LANGUAGE RankNTypes #-}

Подробнее об этом здесь: http://www.haskell.org/haskellwiki/Rank-N_types

5 голосов
/ 19 августа 2011

Есть ли для этого особая причина?

Действительно, есть один. Чтобы сказать это в одном предложении: вывод типа не будет работать должным образом, если снять ограничение системы HM, что лямбда-аргументы должны быть мономорфными.

Саймон Пейтон Джонс разработал проверку типов, которая расширяет HM и допускает полиморфизм более высокого ранга. Но в таких случаях требуются явные аннотации типов. См. Ответ Sjoerds.

2 голосов
/ 19 августа 2011

f в области действия функции p f отличается от функции верхнего уровня f.Это любая функция, указанная в качестве аргумента при вызове p.Поскольку вы не дали p сигнатуру типа, компилятор должен определить, что f, исходя из того, как вы его используете.Ваше первое использование подразумевает, что f :: M () -> M (), что означает p :: (M () -> M ()) -> a.Как сказал кто-то другой, вы должны дать p явную подпись, используя forall, чтобы сделать то, что вы пытаетесь сделать.

...