我已定义了自定义错误类型:
data Failure = NetworkError Message |
UserIsTooStupid Message |
InvalidOperation Message |
UnexpectedError Message
type Message = String
我正在尝试将MonadError
与我的错误类型一起使用:
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
换句话说,此函数可以返回满足MonadIO
和MonadError
的任何monad,但它可以抛出的唯一错误类型是Failure
。
无法编译,错误消息:
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希望我打开FlexibleContexts
语言扩展程序以使此代码正常工作。 FlexibleContexts
做了什么,在我的案例中真的是否必要?我不想在不知道是否需要语言扩展的情况下不打开语言扩展。
如果我不使用类型签名,该函数编译正常,但我不想这样做。
答案 0 :(得分:20)
Haskell 98不允许看起来像MonadError Failure m
的约束,它们必须看起来像MonadError a m
。然而,具有这种约束的能力被添加到GHC,这就是扩展所做的。我理解对语言扩展保持警惕,但FlexibleContexts非常无害。
我相信在Haskell 98出现的时候,使用类似约束的类型理论还没有被开发出来,但从那时起它就有了。扩展在类型检查器中打开了一些使用该理论的额外代码。如果你稍微google一下,你可能会找到一篇关于它如何运作的论文。