为什么单子变换器没有限制产生单子?

14
MonadTrans 类中:
class MonadTrans t where
    -- | Lift a computation from the argument monad to the constructed monad.
    lift :: Monad m => m a -> t m a

为什么t m不被限制为一个Monad呢?也就是说,为什么不这样做:

{-# LANGUAGE MultiParamTypeClasses #-}
class Monad (t m) => MonadTrans t m where
  lift :: Monad m => m a -> t m a

如果答案是“因为这就是它的方式”,那没问题——但对于一个新手来说,这会让人感到困惑。

如果 t 是一个单子类型,它的种类必须是 * -> *,而 t m a 的种类是 * -> * -> *,我认为(也许我错了)。 - Dmytro Sirenko
将单子操作应用于“t m”的“值”是否有意义?是否存在“t m”的值? - Dmytro Sirenko
1
@EarlGray,没有。t m的种类是* -> *,而类型(即值的类型)的种类是*。但同样也没有意义将monad操作应用于Maybe的"值",因为根本没有值。 - luqui
@MattFenwick 在您的建议中,“Monad m”的约束条件需要放入类头中,您不能在成员函数中约束类参数。 - Daniel Fischer
6
为什么类的定义是这样的而不是按照你的方式,至少对于transformers包来说,原因之一是可移植性。 MPTC是一种语言扩展,因此使用它们是不可移植的。 - Daniel Fischer
显示剩余4条评论
1个回答

14
您提出了以下建议:
class Monad (t m) => MonadTrans t m where
    lift :: Monad m => m a -> t m a

...但这是否真的是您想要的意思呢?看起来您想表达的是“如果对于所有m :: * -> *,其中mMonad的实例,则类型t可以是MonadTrans的实例,那么t m也必须是Monad的实例”。

上面的类定义实际上更像是“如果特定类型tm,则t mMonad的实例,则类型tm可以构成MonadTrans的实例”。请仔细考虑其差异以及可能导致不符合预期的实例。

在一般情况下,类型类的每个参数都是独立的“参数”,这是使用MPTCs时头痛和GHC扩展的丰富来源。

这并不是说不能使用这样的定义-正如您指出的那样,当前的定义也不理想。古老的问题“为什么Data.Set不是一个Functor”与此相关,并且此类问题有助于激发最近的ConstraintKinds混乱。

这里“为什么不”的最终答案几乎肯定是Daniel Fischer在评论中给出的答案-因为MonadTrans是相当核心的功能,所以使其依赖于一系列越来越深奥的GHC扩展是不可取的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接