我可以编写这样的多态函数吗?我需要哪些语言扩展?

时间:2013-09-03 11:01:45

标签: haskell

我尝试编写了几个正确解析数字的函数,并对String -> Maybe a的某些集合进行了所有检查(签名a)。我编写了解析无界IntegermaybeReadInteger)的函数,然后我想编写解析所有有界整数类型的多态函数:只需在某些范围检查中包装maybeReadIntegermaybeReadNum试图编写这样的函数,但它没有进行类型检查。可以像这样写吗?我必须打开哪些语言扩展(如果有的话)?

splitIntoSignAndDigits str = case str of 
    '-':rest -> (-1, rest)
    '+':rest -> ( 1, rest)
    _        -> ( 1, str )

maybeReadUnsignedInteger []         = Nothing
maybeReadUnsignedInteger str@(x:xs) = go 0 str
    where go n str = case str of
              []                            -> Just n
              (x:xs) | '0' <= x && x <= '9' -> go (10 * n + digit) xs
                     | otherwise            -> Nothing
                  where digit = toInteger (ord x - ord '0')

maybeReadInteger str = fmap (sign*) (maybeReadUnsignedInteger str')
    where (sign, str') = splitIntoSignAndDigits str

maybeReadNum :: (Integral a, Bounded a) => String -> Maybe a
maybeReadNum = fmap fromInteger . 
               mfilter (\n -> n >= toInteger (minBound :: a) && 
                              n <= toInteger (maxBound :: a)) .
               maybeReadInteger

这样的单态函数:

maybeReadInt :: String -> Maybe Int
maybeReadInt = fmap fromInteger . 
               mfilter (\n -> n >= toInteger (minBound :: Int) && 
                              n <= toInteger (maxBound :: Int)) .
               maybeReadInteger

工作正常。

1 个答案:

答案 0 :(得分:5)

问题在于a签名中的minBounda签名中的maybeReadNum不同。您可以通过启用ScopedTypeVariables

来解决此问题
{-# LANGUAGE ScopedTypeVariables #-}

-- We need to use forall explicitly, otherwise ScopedTypeVariables doesn't take effect.
maybeReadNum :: forall a. (Integral a, Bounded a) => String -> Maybe a
maybeReadNum = fmap fromInteger . 
               mfilter (\n -> n >= toInteger (minBound :: a) && 
                              n <= toInteger (maxBound :: a)) .
               maybeReadInteger

另一种方法是使用asTypeOf之类的帮助:

maybeReadNum :: (Integral a, Bounded a) => String -> Maybe a
maybeReadNum = fmap ((`asTypeOf` minBound') . fromInteger) . 
               mfilter (\n -> n >= toInteger minBound' && 
                              n <= toInteger maxBound') .    
               maybeReadInteger
  where minBound' = minBound
        maxBound' = maxBound `asTypeOf` minBound'

asTypeOf定义如下:

asTypeOf :: a -> a -> a
asTypeOf = const

const具有更受限制的类型siganture。这可以用来断言两个表达式的类型应该是相同的,这有助于编译器推断出正确的类型。

在上面的代码中,asTypeOf用于断言minBound'应该与fromInteger的结果具有相同的类型,而fromInteger的结果必须等于来自签名的a maybeReadNum。由此,编译器推断minBound'的类型为a。然后maxBound'也应该具有相同的类型,因此它也会获得类型a。这有点困难,使用ScopedTypeVariables可能更容易也更好,但它可能是一种在编译器之间保留可移植性的方法(如果你还没有使用某些扩展)。

相关问题