3

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

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

工作正常。

4

1 回答 1

5

问题是a您的签名中minBound的 与a的签名中的不同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具有更受限制的类型签名。这可以用来断言两个表达式的类型应该相同,这有助于编译器推断出正确的类型。

在上面的代码中,asTypeOf用于断言minBound'应该与 fromInteger 的结果具有相同的类型,而后者又必须等于afrom 的签名maybeReadNum。由此,编译器推断minBound'是 类型a。thenmaxBound'也应该有相同的类型,所以它也得到了 type a。这有点困难,而且使用它可能更容易和更好ScopedTypeVariables,但是它可能是一种在编译器之间保持可移植性的方法(如果您还没有使用某些扩展)。

于 2013-09-03T12:05:05.050 回答