如何通过包装ExceptT的newtype合理实现MonadError?
问题解答
你的需求是可以实现的,核心思路是把BlahT内部的ExceptT拆解出来,针对底层m的错误进行捕获处理,而ExceptT自身返回的Left值视为正常结果不干预。
完整的实现代码如下:
import Control.Monad.Trans.Except (ExceptT(..), runExceptT) import Control.Monad.Error.Class (MonadError(..)) newtype BlahT e m a = BlahT (ExceptT e m a) deriving newtype (Functor, Applicative, Monad) instance MonadError e m => MonadError e (BlahT e' m) where throwError = BlahT . ExceptT . fmap Right . throwError catchError (BlahT et) handler = BlahT . ExceptT $ catchError (runExceptT et) (runExceptT . handler)
实现说明
- throwError的实现:你原来的写法是正确的——把底层
m抛出的错误e,转换成m (Either e' a)类型(用Right包裹错误对应的结果),再逐层包装成ExceptT和BlahT。 - catchError的实现:
- 先拆解
BlahT得到内部的ExceptT et,通过runExceptT et拿到底层m的计算结果m (Either e' a)。 - 对这个
m计算使用底层的catchError:如果m在运行过程中抛出了e类型的错误,就调用传入的handler e得到BlahT类型的结果,再通过runExceptT转换成m (Either e' a);如果m正常运行完成(不管返回的是Left e'还是Right a),则直接保留原结果。 - 最后把处理后的
m (Either e' a)重新包装成ExceptT和BlahT,返回符合类型要求的结果。
- 先拆解
这样就完全符合你的需求:只有底层m抛出错误时才触发catchError的处理逻辑,ExceptT自身返回的Left值会被直接保留,不会被catchError干预。
内容的提问来源于stack exchange,提问作者Clinton
相关产品推荐
相关产品推荐

