满足' A唯一确定B'在Haskell中使用功能依赖项时

时间:2018-02-07 14:51:25

标签: haskell ghc functional-dependencies

我正在尝试写一个'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

2 个答案:

答案 0 :(得分:0)

问题在于,虽然m -> e它不遵循t m -> e,因为编译器对t可能做的事情一无所知。

你定义的实际上并不是monad变换器,它是一类Logger monad。通常,接近此问题的mtl方法是:

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

  2. 定义newtype LoggerT e m a = LoggerT(runLoggerT :: m <...>)。这是你的monad变压器。 runLoggerT解包LoggerT操作并返回内部monad m中的值。它返回的详细信息取决于您。

  3. Monad创建MonadTransMonadLoggerLoggerT的实例。

  4. 定义type Logger e = Logger e Identity

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

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

答案 1 :(得分:0)

非常感谢@Carl,这个问题已经解决了 当我打开语言扩展程序不可判定的实例{-# LANGUAGE UndecidableInstances #-})时,此错误消息消失。
虽然我仍然想知道为什么需要这个扩展,现在,它确实使代码编译。