My intention is simple. I want to wrap functions of type a -> b into String -> String (so that a bunch of heterogeneous functions can be put in a list). So I write:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
However, ghc complaints:
Could not deduce (Read a1) arising from a use of `read'
from the context (Read a, Show b)
bound by the type signature for
wrap :: (Read a, Show b) => (a -> b) -> String -> String
I want to know why my piece of code won't work and what kind of hacks is needed to achieve my goal?
Thanks.
Your code won't work because Haskell doesn't re-use or scope type variables; the a in wrap :: (Read a, Show b) => (a -> b) -> (String -> String) is a completely different a from the one in read s :: a (and they're both universally quantified). This is the source of the a1 in the error message; GHC is alpha-converting the program to
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a1)
However, f's argument type is fixed inside wrap, so simply removing the type annotation works fine. Your function becomes
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
-- Or wrap f = show . f . read
And you can use it:
ghci> map ($ "42") [wrap (+ (7 :: Integer)), wrap (* (2.0 :: Double))]
["49","84.0"]
Note that this means that read s has a type you can't write down. In Haskell 2010 (or 98), the only way to get around this is to use a function like asTypeOf :: a -> a -> a; asTypeOf is just const, but thanks to its type signature, it constrains its first, returned, argument to be of the same type as its second. Then, of course, you'd have to conjure up a variable of type a. The following would work for that:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s `asTypeOf` fInput)
where fInput = undefined
fOutput = f fInput -- I still can't give this a type signature
In GHC, to avoid this, you can turn on the ScopedTypeVariables extension; with that on, if you explicitly qualify all your type variables with a forall, they'll be scoped just like value-level names. Then your code would become
{-# LANGUAGE ScopedTypeVariables #-}
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
But remember, you don't need any type annotations at all for this simple example.
To specify the type of read s explicitly, you'll need something like ScopedTypeVariables:
{-# LANGUAGE ScopedTypeVariables #-}
...
wrap :: forall a b. (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s :: a)
Since otherwise the :: a annotation inside the function refers to a different type from the a in the type signature (it implicitly means :: forall a. a). But note that you can also just drop the type annotation entirely:
wrap :: (Read a, Show b) => (a -> b) -> (String -> String)
wrap f = \s -> show $ f (read s)
Since the type of read s can be inferred. That also lets you simplify the body to
wrap f = show . f . read
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With