当前位置: 动力学知识库 > 问答 > 编程问答 >

haskell - Can I write such polymorphic function? What language extensions do I need?

问题描述:

I tried to write a couple of functions that parse numbers properly, with all the checking (of signature String -> Maybe a for some collection of a). I have written function that parses unbounded Integer (maybeReadInteger), and then I wanted to write polymorphic function that parses all bounded integer types: just wrap maybeReadInteger in some range checking. maybeReadNum is attempt to write such function, but it doesn't typecheck. Can it be written like this? What language extensions (if any) I have to turn on?

splitIntoSignAndDigits str = case str of

'-':rest -> (-1, rest)

'+':rest -> ( 1, rest)

_ -> ( 1, str )

maybeReadUnsignedInteger [] = Nothing

maybeReadUnsignedInteger [email protected](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

Monomorphic function like this:

maybeReadInt :: String -> Maybe Int

maybeReadInt = fmap fromInteger .

mfilter (\n -> n >= toInteger (minBound :: Int) &&

n <= toInteger (maxBound :: Int)) .

maybeReadInteger

works OK.

网友答案:

The problem is that the a in your signature for minBound is not the same as the a in the signature of maybeReadNum. You can fix this by turning on 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

An alternative is to use a helper like 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 is defined as follows:

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

It is just const with a more restricted type siganture. This can be used to assert the the the types of two expressions should be the same, which helps the compiler to infer the right types.

In the above code, asTypeOf is used to assert that minBound' should have the same type as the result of fromInteger, which in turn must equal to the a from the signature of maybeReadNum. From this, the compiler infers that minBound' is of type a. Then maxBound' should also have the same type, so it also gets the type a. This is a bit more difficult, and it's probably easier and better to just use ScopedTypeVariables, it might however be a method to retain portable across compilers (in case you aren't already using some extensions).

分享给朋友:
您可能感兴趣的文章:
随机阅读: