问题描述:

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).