我正在尝试写一个'logger'Monad Transformer。其他Monad变形金刚将被应用于它,形成一个更复杂的monad。我希望记录器功能适用于所有这些monad,所以我写了一个类型类如下。
class Logger e m | m -> e where
logMessage :: e -> m ()
我在这里使用功能依赖关系的原因是,monad m
将显式包含类型e
(与State
monad一样),它代表消息类型。
变换器ET
是类型类Logger的一个实例。
data ET e m a = ET { ... }
instance Monad m => Monad (ET e m) where
logMessage msg = ...
instance Monad m => Logger e (ET e m) where
logMessage msg = ...
现在,我希望monad T1 (T2 ... (ET m))
(变换器链中有ET
)是类型类Logger
的实例,但它无法编译。下面是代码。
instance (Logger e m, MonadTrans t) => Logger e (t m) where
logMessage = lift . logMessage
我认为,由于t
只是Monad Transformer,m
可以保证唯一确定e
,因此t m
也应唯一确定e
。但编译器似乎有不同的想法。
Test.hs:43:10: error:
? Illegal instance declaration for ‘Logger e (t m)’
The coverage condition fails in class ‘Logger’
for functional dependency: ‘m -> e’
Reason: lhs type ‘t m’ does not determine rhs type ‘e’
Un-determined variable: e
Using UndecidableInstances might help
? In the instance declaration for ‘MonadException e (t m)’
|
43 | instance (Logger e m, MonadTrans t) => Logger e (t m) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
任何人都可以解释扩展FunctionalDependencies
的工作原理,以及如何解决这个问题?
我在Windows 10上使用的编译器是The Glorious Glasgow Haskell Compilation System, version 8.2.2
。
答案 0 :(得分:0)
问题在于,虽然m -> e
它不遵循t m -> e
,因为编译器对t
可能做的事情一无所知。
你定义的实际上并不是monad变换器,它是一类Logger
monad。通常,接近此问题的mtl方法是:
定义class (Monad m) => MonadLogger e m | m -> e
(只需重命名现有的课程)。
定义newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>)
。这是你的monad变压器。 runLoggerT解包LoggerT操作并返回内部monad m
中的值。它返回的详细信息取决于您。
为Monad
创建MonadTrans
,MonadLogger
和LoggerT
的实例。
定义type Logger e = Logger e Identity
为所有其他mtl monad变换器创建归纳实例。
如果您查看mtl库中的示例,您应该能够看到它是如何完成的。
答案 1 :(得分:0)
非常感谢@Carl,这个问题已经解决了
当我打开语言扩展程序不可判定的实例({-# LANGUAGE UndecidableInstances #-}
)时,此错误消息消失。
虽然我仍然想知道为什么需要这个扩展,现在,它确实使代码编译。