newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
instance (Monad m, Error e) => Monad (ErrorT e m) where
return a = ErrorT $ return (Right a)
m >>= k = ErrorT $ do
a <- runErrorT m
case a of
Left l -> return (Left l)
Right r -> runErrorT (k r)
fail msg = ErrorT $ return (Left (strMsg msg))
让我们考虑上面的代码。理解它让我很麻烦。它如何在Monads / Transormer Monads的背景下工作?特别是,问题在于理解为什么return a = ErrorT $ return (Right a)
返回return (Right a)
。我不明白为什么会这样。
第二个问题。 m >>= k
基本上是我理解的(我希望如此;))但为什么Right r -> runErrorT (k r)
出现了runError?
答案 0 :(得分:3)
ErrorT
只是一个包装器。也就是说,类型ErrorT e m a
与m (Either e a)
类型同构:后者可以由构造函数ErrorT
转换为前者,而其关联的析构函数runErrorT
在其他方向。
return
和>>=
的定义必须包装/解包值以尊重类型。这个包装/解包在运行时没有具体的影响,它只是在那里,所以程序类型检查,编译器可以选择正确的monad实例(m
或ErrorT e m
,具体取决于它是否是一个值裹)。
下面
return a = ErrorT $ return (Right a)
部分return (Right a)
的类型为m (Either e a)
,但我们需要ErrorT e m a
。应用ErrorT
修复了该问题。
同样,在此代码中:
m >>= k = ErrorT $ do
a <- runErrorT m
case a of
Left l -> return (Left l)
Right r -> runErrorT (k r)
我们需要返回ErrorT m e b
。这是通过将ErrorT
应用于某些m (Either e b)
类型值来完成的。部分Left l
的类型为Either e b
,因此return
会将最后的m
添加到顶部。相反,部分k r
的类型为ErrorT e m b
,因此我们需要使用runErrorT
打开它(以便可以再次重新整理do
。)