为什么定义MonadReader需要功能依赖?

Lif*_*ang 8 monads haskell type-systems ghc

我只是设法了解了班级的定义 MonadReader

class Monad m => MonadReader r m | m -> r where
...
Run Code Online (Sandbox Code Playgroud)

在阅读Haskell中的函数依赖文档之后,现在我可以理解,它| m -> r指定类型变量r由唯一地确定m。基于到目前为止我所看到的MonadReader的一些典型实例,我认为这一要求是合理的(例如Reader),但在我看来,Reader即使没有该功能依赖项,我们仍然可以定义实例。

我的问题是,为什么在MonadReader的定义中需要功能依赖?从某种意义上说,在没有MonadReader不能正确定义MonadReader的情况下,这在功能上是必需的吗?还是仅是一种限制,限制了MonadReader的使用方式,以便MonadReader的实例都能以某种预期的方式运行?

chi*_*chi 5

需要以对用户更方便的方式进行类型推断。

例如,如果没有 fundep 这将无法编译:

action :: ReaderT Int IO ()
action = do
  x <- ask
  liftIO $ print x
Run Code Online (Sandbox Code Playgroud)

为了使上述编译,我们需要写

action :: ReadertT Int IO ()
action = do
  x <- ask :: ReadertT Int IO Int
  liftIO $ print x
Run Code Online (Sandbox Code Playgroud)

这是因为,如果没有 fundep,编译器就无法推断出它x是一个Int. 毕竟一个 monadReadertT Int IO可能有多个实例

instance MonadReader Int (ReaderT Int IO) where
   ask = ReaderT (\i -> return i)
instance MonadReader Bool (ReaderT Int IO) where
   ask = ReaderT (\i -> return (i != 0))
instance MonadReader String (ReaderT Int IO) where
   ask = ReaderT (\i -> return (show i))
-- etc.
Run Code Online (Sandbox Code Playgroud)

所以程序员必须提供一些强制的注释x :: Int,否则代码是不明确的。