如何通过 monad 转换器参数化 monad?

How can I parameterize a monad by its monad transformer?

我想编写一个可以通过其 monad 转换器参数化的类型。 我已经尝试了几件事,并以这些方式结束:

newtype Foo t a = Foo { unFoo :: t (State St) a }

那么,我想要一堆实例:

deriving instance (MonadTrans t, Monad (t (State St))) => Monad (Foo t)

最后,我使用适当的 monad 转换器实例化该类型。我的代码进行了类型检查,但我收到了所有派生实例的警告:“>>= 没有明确的实现”。如果我尝试 运行 它,当它命中类型 class 函数时,我会得到未定义的错误。

有没有办法自动导出我需要的所有实例?我试图推导更多 classes 而不仅仅是 Monad.

我也对用更符合人体工学的方法来完成同样的事情感兴趣——我必须不​​断地手动包装和解开 Foo 构造函数,尤其是当我尝试 lift 计算时进入它,因为我不知道如何为它实现一个 MonadTrans 实例。

Monad 不是股票衍生品 class,如 EqShow。顺便说一下,Functor 也不是,但是您实际上可以通过 DeriveFunctor 启用那个。但是 Monad.

就没有这样的运气了

但是等等,你的推导实际上以某种方式起作用,对吧?没有错误?

这可能是因为您启用了 DeriveAnyClass,这是一个方便的小扩展,但不是特别聪明的扩展。它不能为您完成繁重的工作,它所做的只是生成一个空的实例声明。根本没有方法实现。

等等,那有什么用呢?因为默认方法!许多 classes 为任何类型提供默认方法实现,或者至少有一些约束。我最常遇到的是 FromJSONToJSON,它们具有所有内容的默认实现,前提是您的类型具有 Generic.

DeriveAnyClass只是语法的稍微缩短而已,仅此而已。一个方便的东西,所以你可以写 data Foo = ... deriving Bar,依赖所有 Bar 的默认方法。

但是 Monad 没有 >>= 的默认实现,这就是您收到警告的原因。当然,一旦您尝试调用该方法就会崩溃。


你真正想要的是GeneralizedNewtypeDeriving。此扩展还允许您自动派生任何 class,但仅限于 newtype,它的工作原理是将每个方法委托给包装类型的实现。非常好用。

这正是您所需要的:Foo 的所有方法实现都将委托给 t 的方法实现。


在您的代码中,根据您的问题描述,我假设您根本没有启用 GeneralizedNewtypeDeriving,或者您同时启用了 DerivedAnyClassGeneralizedNewtypeDeriving , 在这种情况下 DeriveAnyClass 优先(惊喜!)

要解决此问题,您可以禁用 DeriveAnyClass,或者使用 DerivingStrategies 更好。启用该扩展后,您可以明确告诉编译器对每个推导使用哪种策略 - DeriveAnyClassGeneralizedNewtypeDeriving:

{-# LANGUAGE DerivingStrategies #-}

deriving newtype instance (MonadTrans t, Monad (t (State St))) => Monad (Foo t)
         ^^^^^^^
            |
    this is the important bit

P.S。如果两个扩展都启用了,你应该也会收到一个警告。