如何通过 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,如 Eq
或 Show
。顺便说一下,Functor
也不是,但是您实际上可以通过 DeriveFunctor
启用那个。但是 Monad
.
就没有这样的运气了
但是等等,你的推导实际上以某种方式起作用,对吧?没有错误?
这可能是因为您启用了 DeriveAnyClass
,这是一个方便的小扩展,但不是特别聪明的扩展。它不能为您完成繁重的工作,它所做的只是生成一个空的实例声明。根本没有方法实现。
等等,那有什么用呢?因为默认方法!许多 classes 为任何类型提供默认方法实现,或者至少有一些约束。我最常遇到的是 FromJSON
和 ToJSON
,它们具有所有内容的默认实现,前提是您的类型具有 Generic
.
而DeriveAnyClass
只是语法的稍微缩短而已,仅此而已。一个方便的东西,所以你可以写 data Foo = ... deriving Bar
,依赖所有 Bar
的默认方法。
但是 Monad
没有 >>=
的默认实现,这就是您收到警告的原因。当然,一旦您尝试调用该方法就会崩溃。
你真正想要的是GeneralizedNewtypeDeriving
。此扩展还允许您自动派生任何 class,但仅限于 newtype
,它的工作原理是将每个方法委托给包装类型的实现。非常好用。
这正是您所需要的:Foo
的所有方法实现都将委托给 t
的方法实现。
在您的代码中,根据您的问题描述,我假设您根本没有启用 GeneralizedNewtypeDeriving
,或者您同时启用了 DerivedAnyClass
和 GeneralizedNewtypeDeriving
, 在这种情况下 DeriveAnyClass
优先(惊喜!)
要解决此问题,您可以禁用 DeriveAnyClass
,或者使用 DerivingStrategies
更好。启用该扩展后,您可以明确告诉编译器对每个推导使用哪种策略 - DeriveAnyClass
或 GeneralizedNewtypeDeriving
:
{-# LANGUAGE DerivingStrategies #-}
deriving newtype instance (MonadTrans t, Monad (t (State St))) => Monad (Foo t)
^^^^^^^
|
this is the important bit
P.S。如果两个扩展都启用了,你应该也会收到一个警告。
我想编写一个可以通过其 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,如 Eq
或 Show
。顺便说一下,Functor
也不是,但是您实际上可以通过 DeriveFunctor
启用那个。但是 Monad
.
但是等等,你的推导实际上以某种方式起作用,对吧?没有错误?
这可能是因为您启用了 DeriveAnyClass
,这是一个方便的小扩展,但不是特别聪明的扩展。它不能为您完成繁重的工作,它所做的只是生成一个空的实例声明。根本没有方法实现。
等等,那有什么用呢?因为默认方法!许多 classes 为任何类型提供默认方法实现,或者至少有一些约束。我最常遇到的是 FromJSON
和 ToJSON
,它们具有所有内容的默认实现,前提是您的类型具有 Generic
.
而DeriveAnyClass
只是语法的稍微缩短而已,仅此而已。一个方便的东西,所以你可以写 data Foo = ... deriving Bar
,依赖所有 Bar
的默认方法。
但是 Monad
没有 >>=
的默认实现,这就是您收到警告的原因。当然,一旦您尝试调用该方法就会崩溃。
你真正想要的是GeneralizedNewtypeDeriving
。此扩展还允许您自动派生任何 class,但仅限于 newtype
,它的工作原理是将每个方法委托给包装类型的实现。非常好用。
这正是您所需要的:Foo
的所有方法实现都将委托给 t
的方法实现。
在您的代码中,根据您的问题描述,我假设您根本没有启用 GeneralizedNewtypeDeriving
,或者您同时启用了 DerivedAnyClass
和 GeneralizedNewtypeDeriving
, 在这种情况下 DeriveAnyClass
优先(惊喜!)
要解决此问题,您可以禁用 DeriveAnyClass
,或者使用 DerivingStrategies
更好。启用该扩展后,您可以明确告诉编译器对每个推导使用哪种策略 - DeriveAnyClass
或 GeneralizedNewtypeDeriving
:
{-# LANGUAGE DerivingStrategies #-}
deriving newtype instance (MonadTrans t, Monad (t (State St))) => Monad (Foo t)
^^^^^^^
|
this is the important bit
P.S。如果两个扩展都启用了,你应该也会收到一个警告。