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

我想编写一种可以通过其 monad 转换器参数化的类型。我尝试了几件事,最终得到了一些类似的东西:

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

然后,我想要一堆实例:

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

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

有没有办法自动派生我需要的所有实例?我试图派生出更多的类,而不仅仅是Monad.

我也对一种更符合人体工程学的方式来完成同样的事情感兴趣——我必须不​​断手动包装和解开Foo构造函数,特别是当我尝试对其lift进行计算时,因为我无法弄清楚如何实现一个MonadTrans实例为了它。

回答

Monad不是股票可推导的类,例如EqShowFunctor顺便说一句,两者都不是,而是您实际上可以通过DeriveFunctor. 但没有这样的运气Monad

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

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

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

而且DeriveAnyClass是语法,这一切都只是轻微缩短。一个方便的东西,所以你可以写data Foo = ... deriving Bar,依赖于所有Bar的默认方法。

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


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

这正是您所需要的:所有方法实现 forFoo都将委托给那些 for t


在你的代码中,我从你的问题的描述假定,你要么是没有GeneralizedNewtypeDeriving在所有启用的,要不然你同时DerivedAnyClassGeneralizedNewtypeDeriving启用,在这种情况下DeriveAnyClass优先(惊喜!)

为了解决这个问题,您可以禁用DeriveAnyClass,或者更好的是,使用DerivingStrategies。启用该扩展后,您可以明确告诉编译器对每个派生使用哪种策略 -DeriveAnyClass或者GeneralizedNewtypeDeriving

{-# LANGUAGE DerivingStrategies #-}

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

PS 如果启用了两个扩展,您还应该收到警告。


以上是如何通过monad转换器参数化monad?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>