Ссылочная прозрачность с полиморфизмом в Haskell

Скажем, у меня есть функция:

f :: Int -> (Rational, Integer)
f b = ((toRational b)+1,(toInteger b)+1)

Я хочу абстрагироваться от (+1) следующим образом:

f :: Int -> (Rational, Integer)
f b = (h (toRational b)
      ,h (toInteger b))
    where h = (+1)

Очевидно, это не сработает, но если я укажу сигнатуру типа, она сработает:

f :: Int -> (Rational, Integer)
f b = (h (toRational b)
      ,h (toInteger b))
    where h :: Num a => a -> a
          h = (+1)

Скажем, теперь я хочу еще больше абстрагировать функцию, передав h в качестве параметра:

f :: Num a => Int -> (a -> a) -> (Rational, Integer)
f b g = (h (toRational b)
        ,h (toInteger b))
    where h :: Num a => a -> a
          h = g

Я получаю сообщение об ошибке, что внутреннее a не совпадает с внешним a.

Кто-нибудь знает, как правильно написать эту функцию? Я хочу передать полиморфную функцию g в f и использовать ее полиморфно.

Я сталкивался с этой ситуацией несколько раз в самых разных проектах, и я не мог найти хорошего решения.


person nulvinge    schedule 14.02.2013    source источник
comment
Добро пожаловать в мир типов с более высоким рейтингом!   -  person Ingo    schedule 14.02.2013
comment
Очевидно, что это не сработает На самом деле, не очевидно, что это не сработает: (+1) полиморфен, только ужасный мономорфизм ограничение не позволяет h наследовать эту черту. Если вы установите -XNoMonomorphismRestriction, ваше второе поле кода будет работать нормально.   -  person leftaroundabout    schedule 14.02.2013


Ответы (1)


Я нашел решение: используя квантификатор forall следующим образом:

{-# LANGUAGE RankNTypes #-}
f :: Int -> (forall a. Num a=> a -> a) -> (Rational, Integer)
f b g = (h (toRational b)
        ,h (toInteger b))
    where h :: Num a => a -> a
          h = g

Что, конечно, можно превратить в:

f :: Int -> (forall a. Num a=>a -> a) -> (Rational, Integer)
f b g = (g (toRational b)
        ,g (toInteger b))
person nulvinge    schedule 14.02.2013
comment
Это точно. Без forall Haskell попытается специализировать аргумент функции внутри тела функции. Это приводит к неразрешимой системе (Rational ~ a) && (Integer ~ a). Привязка forall говорит, что вы собираетесь передать неспециализированную функцию и специализировать ее только на месте вызова. Недостатком является то, что вы не можете передать другой тип a и убедиться, что он соответствует функции — не то, чтобы вы сделали это в этой ситуации. - person J. Abrahamson; 14.02.2013