有人可以解释
之间的区别ErrorT String Identity Integer
和Either String Integer
?
答案 0 :(得分:3)
(为了简化这一点,我将根据ExceptT
类型而不是the deprecated ErrorT
来回答。我对ErrorT
的回答并不严格,但是&# 39; s真实模数导致ErrorT
弃用的一些令人讨厌的事实。)
这里要理解的关键概念是isomorphism。非正式地说非常,两种类型是同构的,尽管表面上有所不同,但它们基本上是相同的。"
我们可以通过添加这个概念来更多地关注这个概念:两个Haskell类型是同构的,如果两者都可以转换为另一个"无损"时尚由一对反函数。在这种情况下,Either e a
和EitherT e Identity a
是同构的,因为以下两个函数是反转的:
toEither :: ExceptT e Identity a -> Either e a
toEither ma = runIdentity (runExceptT ma)
toExceptT :: Either e a -> ExceptT e Identity a
toExceptT (Left e) = throwError e
toExceptT (Right a) = return a
通过上面的非正式评论,这告诉你的是这两种类型基本相同。"反函数增加的肉是他们证明:
所以归结为在编程中通常有很多不同的方法来完成 相同的事情。在这种情况下,ExceptT
是Either
的更一般版本,但是当您插入Identity
作为基本monad时,您会获得与Either
完全相同的内容。在这种情况下,我们通常更喜欢使用Either
因为我们没有那么多的样板,但有时我们发现自己处于ExceptT e Identity a
的情况,然后知道同构有助于您了解它与Either e a
没有本质区别。
这是另一种分析方法。 ExceptT
类型的定义如下:
newtype ExceptT e m a = ExceptT { runExceptT :: m (Either e a) }
在Haskell中,newtype
定义是同构(ExceptT
构造函数而runExceptT
函数是反转的),在这种情况下意味着以下两种类型是同构的:
ExceptT e m a ~ m (Either e a)
这意味着这两个同构也是同构的:
ExceptT e Identity a ~ Identity (Either e a)
但Identity
也被定义为newtype
:
newtype Identity a = Identity { runIdentity :: a }
这意味着这种同构也存在:
Identity a ~ a
因此这些也是如此:
Identity (Either e a) ~ Either e a
ExceptT e Identity a ~ Either e a
因此,当一个库透明地将某种类型定义为newtype
时,注意到这一事实是值得的。 (这就是为什么Haskell中的库文档告诉您导出构造函数的数据类型是data
还是newtype
定义 - 知道它是newtype
的原因很重要。)
从同构的角度来看这个问题的另一个好处是,它可以帮助您更好地理解许多标准的Haskell库。如上所述,根据newtype
的{{1}}定义,以下同构成立
ExceptT
使用ExceptT e m a ~ m (Either e a)
形式的monadic类型时,我们通常会将m a
称为" monad"和m
"结果类型"。通过这个镜头观察它,然后:
a
中,m (Either e a)
是monad,m
是结果类型; Either e a
中,ExceptT e m a
是monad,ExceptT e m
是结果类型。所以a
monad变换器是一个同构,它允许我们翻转视角"对ExceptT
类型的计算,以便我们将m (Either e a)
视为结果类型而不是a
。它通过改变含义"来实现这一点。 monad操作从他们在Either e a
中的含义到解释"视角翻转"问题。
答案 1 :(得分:1)
By convention, Either
is used to emulate an exception system, but it is not designed for that.
That's why we use ErrorT
for bigger implementations, since it comes with the Error
type, which allows the user to define more general exceptions.
However, once you run with runErrorT
the monadic action of type ErrorT e Identity a
, you get a value of type Either e a
. So, those types are somewhat equivalent, just that ErrorT
allows you to use functions like throwError
and catchError
instead of either
, which is less clear (especially when repeatedly used).
PS: As the documentation states, Control.Monad.Error
is deprecated, Control.Monad.Except
instead.