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