[英]MonadError instance for a Free Monad
I have created a very useful Free Monad out of a sum data type. 我用sum数据类型创建了一个非常有用的Free Monad。 That abstracts access to a persistent data store:
这抽象了对持久性数据存储的访问:
data DataStoreF next =
Create Asset ( String -> next)
| Read String ( Asset -> next)
| Update Asset ( Bool -> next)
| UpdateAll [Asset] ( Bool -> next)
| Delete Asset ( Bool -> next)
| [...] -- etc. etc.
| Error String
type DataStore = Free DataStoreF
I would like to make DataStore
an instance of MonadError
with the error message handled as (Free (Error str))
: 我想使
DataStore
成为MonadError
一个实例, MonadError
错误消息处理为(Free (Error str))
:
instance MonadError String DataStore where
throwError str = errorDS str
catchError (Free (ErrorDS str)) f = f str
catchError x _ = x
But I am running into Overlapping Instances errors. 但我遇到了Overlapping Instances错误。
What is the proper way to make the DataStore
monad and instance of MonadError
? 制作
DataStore
monad和MonadError
实例的正确方法是什么?
The Free
type already provides a MonadError
instance for all free monads: Free
类型已经为所有免费monad提供了MonadError
实例:
instance (Functor m, MonadError e m) => MonadError e (Free m) where { ... }
When you write type DataStore = ...
, you are simply defining a type alias , which is basically a type-level macro. 当你写
type DataStore = ...
,你只是定义一个类型别名 ,它基本上是一个类型级别的宏。 All uses of the DataStore
type are replaced with its definition. DataStore
类型的所有用法都将替换为其定义。 This means that using DataStore
is indistinguishable from using Free DataStoreF
directly, so when you do this: 这意味着使用
DataStore
与直接使用Free DataStoreF
无法区分,因此当您执行此操作时:
instance MonadError String DataStore where { ... }
…you are actually doing this: ......你实际上是这样做的:
instance MonadError String (Free DataStoreF) where { ... }
…and that conflicts with the instance defined above. ......并且与上面定义的实例冲突。
To circumvent that, you should define a newtype
to produce an entirely fresh type that can have its own instances on it, unrelated to the ones defined on Free
. 为了避免这种情况,你应该定义一个
newtype
来生成一个完全新鲜的类型,它可以有自己的实例,与Free
定义的实例无关。 If you use the GeneralizedNewtypeDeriving
extension, you can avoid a lot of the boilerplate that would otherwise be required by a separate newtype
: 如果使用
GeneralizedNewtypeDeriving
扩展,则可以避免使用单独的newtype
所需的许多样板:
{-# LANGUAGE GeneralizedNewtypeDeriving -}
data DataStoreF next = ...
newtype DataStore a = DataStore (Free DataStoreF a)
deriving (Functor, Applicative, Monad)
instance MonadError String DataStore where { ... }
This should avoid the overlapping instance problem without the need to write out all the Functor
, Applicative
, and Monad
instances manually. 这应避免重叠实例问题,而无需手动写出所有
Functor
, Applicative
和Monad
实例。
Your instance and the instance given by the library: 您的实例和库提供的实例:
instance (Functor m, MonadError e m) => MonadError e (Free m)
are indeed overlapping, but this does not mean that they are incompatible. 确实是重叠的,但这并不意味着它们是不相容的。 Note that the above instance is 'more general' in a sense than yours - any type which would match your instance would match this one.
请注意,上述实例在某种意义上比您的更“一般” - 任何与您的实例匹配的类型都会匹配此实例。 When one uses the
OverlappingInstances
extension (or with modern GHC, an {-# OVERLAP{S/PING/PABLE} #-}
pragma), instances may overlap, and the most specific ( least general) instance will be used. 当使用
OverlappingInstances
扩展(或使用现代GHC, {-# OVERLAP{S/PING/PABLE} #-}
编译指示)时,实例可能会重叠,并且将使用最具体( 最不通用)的实例。
Without the extension, eg throwError "x" :: DataStore ()
gives the type error: 没有扩展名,例如
throwError "x" :: DataStore ()
给出类型错误:
* Overlapping instances for MonadError [Char] (Free DataStoreF)
arising from a use of `throwError'
Matching instances:
instance [safe] (Functor m, MonadError e m) =>
MonadError e (Free m)
-- Defined in `Control.Monad.Free'
instance [safe] MonadError String DataStore
but with the addition of a pragma 但是增加了一个pragma
instance {-# OVERLAPS #-}
MonadError String DataStore where
the expression throwError "x" :: DataStore ()
still matches both instances, but since one is more specific than the other (the one you wrote) it is selected: 表达式
throwError "x" :: DataStore ()
仍然匹配两个实例,但由于一个比另一个(您编写的那个)更具体,因此选择它:
>throwError "x" :: DataStore ()
Free (Error "x")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.