“Ambigous type variable” error when defining custom “read” function
-
22-09-2019 - |
Question
While trying to compile the following code, which is enhanced version of read build on readMay from Safe package.
readI :: (Typeable a, Read a) => String -> a
readI str = case readMay str of
Just x -> x
Nothing -> error ("Prelude.read failed, expected type: " ++
(show (typeOf > (undefined :: a))) ++
"String was: " ++ str)
I get an error from GHC:
WavefrontSimple.hs:54:81:
Ambiguous type variable `a' in the constraint:
`Typeable a'
arising from a use of `typeOf' at src/WavefrontSimple.hs:54:81-103
Probable fix: add a type signature that fixes these type variable(s)`
I don't understand why. What should be fixed to get what I meant?
EDIT: Ok, so the solution to use ScopedTypeVariables
and forall a
in type signature works. But why the following produces very similar error to the one above? The compiler should infer the right type since there is asTypeOf :: a -> a -> a
used.
readI :: (Typeable a, Read a) => String -> a
readI str = let xx = undefined in
case readMay str of
Just x -> x `asTypeOf` xx
Nothing -> error ("Prelude.read failed, expected type: "
++ (show (typeOf xx)) ++
"String was: " ++ str)
Solution
The latter doesn't work because the type of xx
is the same as the type of undefined
-- i.e., "forall a. a." The fact that you force xx to be used with one concrete type with the asTypeOf operator doesn't mean that it is less polymorphic everywhere else.
OTHER TIPS
The a
in undefined :: a
and readI :: (Typeable a, Read a) => String -> a
are not the same type a
. It's as if you wrote readI :: ... a; readI = ... (undefined :: b)
.
{-# LANGUAGE ScopedTypeVariables #-}
readI :: forall a. (Typeable a, Read a) => String -> a
...
The scoped type variables extension changes the Haskell language to allow you to carry the type variable a
from outer scope to inner scope, if explicitly quantified with forall
.
I'm not sure why your x `asTypeOf` xx
doesn't seem to work. This does, though:
readI :: (Typeable a, Read a) => String -> a
readI str = xx where
xx = case readMay str of
Just x -> x
Nothing -> error ("Prelude.read failed, expected type: "
++ (show (typeOf xx)) ++
"String was: " ++ str)
I think you need scoped type variables.
{-# LANGUAGE ScopedTypeVariables #-}
readI :: forall a. (Typeable a, Read a) => String -> a
readI str = case readMay str of
Just x -> x
Nothing -> error ("Prelude.read failed, expected type: " ++
(show (typeOf > (undefined :: a))) ++
"String was: " ++ str)