Потеря полиморфизма после сопоставления паттернов


Следующий код предназначен для получения либо двойника, либо целого числа. s предполагается либо negate, либо id; n целая часть; и f дробная часть или Nothing для целого числа.

computeValue :: Num a => (a->a) -> Integer -> (Maybe Double) -> Either Double Integer
computeValue s n Nothing = Right $ s n
computeValue s n (Just a) = Left $ s (fromIntegral n + a)

Когда я компилирую это, я получаю:

test1.hs:2:28:
    Couldn't match type `Integer' with `Double'
    Expected type: Either Double Integer
      Actual type: Either Double a
    In the expression: Right $ s n
    In an equation for `computeValue':
        computeValue s n Nothing = Right $ s n

test1.hs:2:38:
    Couldn't match type `Integer' with `Double'
    In the first argument of `s', namely `n'
    In the second argument of `($)', namely `s n'
    In the expression: Right $ s n

Похоже, что компилятор каким-то образом потерял след того факта, что s полиморфен. Что здесь произошло и как мне это исправить?

1   7   2012-05-24 02:45:08

1 ответ:

s не является полиморфным изнутри вашей функции: вы можете использовать любую функцию, которая работает на некоторых Num пример как этот параметр, это может быть функция, которая работает только на Complex! То, что вам нужно, - это универсально квантифицированная функция s, то есть та, которая может быть фактически вызвана с помощью любой Num Пример.

{-# LANGUAGE Rank2Types #-}

computeValue :: (forall a . Num a => a->a) -> Integer -> Maybe Double -> Either Double Integer
computeValue s n Nothing = Right $ s n
computeValue s n (Just a) = Left $ s (fromIntegral n + a)

Это работает тогда:

Prelude Data.Either> computeValue id 3 Nothing
Right 3
Prelude Data.Either> computeValue negate 57 (Just pi)
Left (-60.1415926535898)