Действительно общая функция в Haskell

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

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 варьируется в пределах одного выражения? Я предполагаю, что здесь вступают в игру экзистенциальные типы, но я просто не могу понять, как это сделать.


person Anupam Jain    schedule 19.08.2011    source источник
comment
Зачем вам делать M кортежем? Как некрасиво.   -  person alternative    schedule 20.08.2011
comment
что-то для всех по умолчанию, что-то нет. вы можете увидеть следующие тексты: en.wikibooks.org/wiki/Haskell/Existentially_quantified_types   -  person wuxb    schedule 20.08.2011
comment
@monadic, это всего лишь минимальный пример, который воспроизводит проблему, с которой я столкнулся, в моем реальном коде (который значительно сложнее).   -  person Anupam Jain    schedule 23.08.2011


Ответы (3)


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

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

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

{-# LANGUAGE Rank2Types #-}

or

{-# LANGUAGE RankNTypes #-}

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

person Sjoerd Visscher    schedule 19.08.2011
comment
Благодарю вас! Добавление подписи этого типа сработало. Я подумал, что это как-то связано с ключевым словом forall, но не знал о типах RankN. Кажется, мне есть что почитать! - person Anupam Jain; 19.08.2011

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

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

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

person Ingo    schedule 19.08.2011

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

person Alex    schedule 19.08.2011