I have defined a custom error type:
data Failure = NetworkError Message |
UserIsTooStupid Message |
InvalidOperation Message |
UnexpectedError Message
type Message = String
I am trying to use MonadError
with my error type:
loadJSON :: (Aeson.FromJSON v, MonadIO m, MonadError Failure m) => URI -> m v
loadJSON uri = do
body <- liftIO $ getResponseBody =<< simpleHTTP (getRequest uri)
case Aeson.decode body of
Just json -> return json
Nothing -> throwError $ SerialisationError "Couldn't deserialise from JSON"
type URI = String
In other words, this function can return any monad which satisfies both MonadIO
and MonadError
, but the only type of error it can throw is Failure
.
This fails to compile, with the error message:
Non type-variable argument in the constraint: MonadError Failure m
(Use -XFlexibleContexts to permit this)
In the type signature for `loadJSON':
loadJSON :: (Aeson.FromJSON v, MonadIO m, MonadError Failure m) =>
URI -> m v
GHC wants me to turn on the FlexibleContexts
language extension to make this code work. What does FlexibleContexts
do, and is it really necessary in my case? I prefer not to turn on language extensions willy-nilly without knowing whether I need them.
The function compiles fine if I leave off the type signature, but I prefer not to do that.
Haskell 98 doesn't allow constraints that looks like MonadError Failure m
, they have to look like MonadError a m
. The ability to have constraints like that was added to GHC however, which is what that extension does. I understand being wary of language extensions, but FlexibleContexts is pretty harmless.
I believe at the time Haskell 98 came out, the type theory to use constraints like that hadn't been developed, but since then it has. The extension turns on some extra code in the type checker which uses that theory. If you google around a little you might be able to find a paper on how it works.
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