在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 处理此问题的方法是:

  1. 定义一个class (Monad m) => MonadLogger e m | m -> e(只需重命名现有的class)。

  2. 定义 newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>)。这是你的 monad 转换器。 runLoggerT 展开一个 LoggerT 操作和 returns 内部 monad m 中的一个值。具体是什么returns由你决定。

  3. LoggerT 创建 MonadMonadTransMonadLogger 的实例。

  4. 定义type Logger e = Logger e Identity

  5. 为所有其他 mtl monad 转换器创建归纳实例。

如果您查看 mtl 库中的示例,您应该能够了解它是如何完成的。

非常感谢@Carl,这个问题已经解决了。
当我打开语言扩展 Undecidable Instances(来自 {-# LANGUAGE UndecidableInstances #-})时,此错误消息消失了。
虽然我仍然想知道为什么需要这个扩展,但就目前而言,它确实可以编译代码。