I was working through some examples and came across an error with my implementation of bind (>>=) for the Error monad:
data E a = Success a
| Error String
instance Monad E where
return a = Success a
(Success a) >>= f = f a
e@(Error s) >>= _ = e
Error.hs:15:25:
Couldn't match type `a' with `b'
`a' is a rigid type variable bound by
the type signature for >>= :: E a -> (a -> E b) -> E b
at Error.hs:14:5
`b' is a rigid type variable bound by
the type signature for >>= :: E a -> (a -> E b) -> E b
at Error.hs:14:5
Expected type: E b
Actual type: E a
In the expression: e
In an equation for `>>=': e@(Error s) >>= _ = e
In the instance declaration for `Monad E'
If don't use a named pattern (the @
syntax), everything works:
(Error s) >>= _ = Error s
Why aren't these two forms equivalent? What is going on?
Let's start by looking at the type of Error
:
Error :: String -> E a
That means that for any a
type, you can get an E a
by using something like Error "foo"
. However, each specific Error "foo"
value has to choose a specific a
, and you can't change it afterwards. So Error "foo" :: E Int
is not the same as Error "foo" :: E String
.
So in your specific example, e
is referring to the actual "original" Error s
value, of type E a
, whereas in the alternative formulation you are constructing a new Error s
value, which type inference forces to have type E b
.
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