在Haskell中使用Functional Dependencies时满足'A uniquely determines B'的条件
Satisfy the condition that 'A uniquely determines B' when using Functional Dependencies in Haskell
我正在尝试编写一个“记录器”Monad Transformer。然后将其他 Monad Transformers 应用于它,以形成更复杂的 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
也应该唯一确定[=17] =].但是编译器似乎有不同的想法。
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
的工作原理,以及如何解决这个问题?
我使用的编译器是 The Glorious Glasgow Haskell Compilation System, version 8.2.2
,在 Windows 10.
问题在于,虽然 m -> e
它并没有遵循 t m -> e
因为编译器对 t
可能做什么一无所知。
你定义的实际上不是一个 monad 转换器,它是 class 个 Logger
个 monad。通常,mtl 处理此问题的方法是:
定义一个class (Monad m) => MonadLogger e m | m -> e
(只需重命名现有的class)。
定义 newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>)
。这是你的 monad 转换器。 runLoggerT 展开一个 LoggerT 操作和 returns 内部 monad m
中的一个值。具体是什么returns由你决定。
为 LoggerT
创建 Monad
、MonadTrans
和 MonadLogger
的实例。
定义type Logger e = Logger e Identity
为所有其他 mtl monad 转换器创建归纳实例。
如果您查看 mtl 库中的示例,您应该能够了解它是如何完成的。
非常感谢@Carl,这个问题已经解决了。
当我打开语言扩展 Undecidable Instances(来自 {-# LANGUAGE UndecidableInstances #-}
)时,此错误消息消失了。
虽然我仍然想知道为什么需要这个扩展,但就目前而言,它确实可以编译代码。
我正在尝试编写一个“记录器”Monad Transformer。然后将其他 Monad Transformers 应用于它,以形成更复杂的 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
也应该唯一确定[=17] =].但是编译器似乎有不同的想法。
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
的工作原理,以及如何解决这个问题?
我使用的编译器是 The Glorious Glasgow Haskell Compilation System, version 8.2.2
,在 Windows 10.
问题在于,虽然 m -> e
它并没有遵循 t m -> e
因为编译器对 t
可能做什么一无所知。
你定义的实际上不是一个 monad 转换器,它是 class 个 Logger
个 monad。通常,mtl 处理此问题的方法是:
定义一个
class (Monad m) => MonadLogger e m | m -> e
(只需重命名现有的class)。定义
newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>)
。这是你的 monad 转换器。 runLoggerT 展开一个 LoggerT 操作和 returns 内部 monadm
中的一个值。具体是什么returns由你决定。为
LoggerT
创建Monad
、MonadTrans
和MonadLogger
的实例。定义
type Logger e = Logger e Identity
为所有其他 mtl monad 转换器创建归纳实例。
如果您查看 mtl 库中的示例,您应该能够了解它是如何完成的。
非常感谢@Carl,这个问题已经解决了。
当我打开语言扩展 Undecidable Instances(来自 {-# LANGUAGE UndecidableInstances #-}
)时,此错误消息消失了。
虽然我仍然想知道为什么需要这个扩展,但就目前而言,它确实可以编译代码。