Search code examples
haskellghcfunctional-dependencies

Satisfy the condition that 'A uniquely determines B' when using Functional Dependencies in Haskell


I am trying to write a ‘logger’ Monad Transformer. Other Monad Transformers will then be applied to it, to form a more complex monad. I want the logger function to work on all these monads, so I wrote a typeclass as follows.

class Logger e m | m -> e where
    logMessage :: e -> m ()

The reason I use Functional Dependencies here, is that the monad m will explicitly contain the type e (as what it is with State monad), which stands for the message type.
The transformer ET is made an instance of typeclass 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 = ...

Now, I want the monad T1 (T2 ... (ET m)) (which has an ET in the transformer chain) to be an instance of typeclass Logger, but it failed to compile. Below is the code.

instance (Logger e m, MonadTrans t) => Logger e (t m) where
    logMessage = lift . logMessage

I thought that since t is only a Monad Transformer, and m is guaranteed to uniquely determine e, then t m should also uniquely determine e. But the compiler seems to think differently.

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.

Can anyone explain how the extension FunctionalDependencies works, as well as how to solve this problem?
The compiler I use is The Glorious Glasgow Haskell Compilation System, version 8.2.2, on Windows 10.


Solution

  • Many thanks to @Carl, this problem is solved.
    When I turned on the language extension Undecidable Instances (by {-# LANGUAGE UndecidableInstances #-}), this error message disappeared.
    Though still I wonder why this extension is needed, for now, it really makes the code compile.