如何在多Monad约束下使用Haskell的do表示法?
Haskell Monad 问题:Pandoc过滤器调用错误分析与解决
问题背景
刚接触Haskell,对Monad的直观理解不够深入,尝试编写调用Pandoc Lua过滤器的函数时遇到类型错误。
我的实现代码
applyPandocFilters :: (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc applyPandocFilters = do luaEngine <- getEngine applyFilters luaEngine Environment{envReaderOptions = readerOptions, envWriterOptions = writerOptions} [ LuaFilter "./filters/lua/filters.lua" , CiteprocFilter ] []
依赖的applyFilters类型签名
applyFilters :: (PandocMonad m, MonadIO m) => ScriptingEngine -> Environment -> [Filter] -> [String] -> Pandoc -> m Pandoc
遇到的错误信息
• Could not deduce ‘MonadIO ((->) Pandoc)’ arising from a use of ‘getEngine’ from the context: (MonadIO m, PandocMonad m) bound by the type signature for: applyPandocFilters :: forall (m :: * -> *). (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc
问题原因
当前写法中,applyPandocFilters直接将do块赋值给函数,Haskell会默认把这个do块放在(->) Pandoc(Reader Monad)上下文中执行,但(->) Pandoc并没有实现MonadIO类型类,因此报错无法推导该约束。
本质上,函数类型Pandoc -> m Pandoc等价于(->) Pandoc (m Pandoc),但你需要的是在m上下文里执行getEngine,再将Pandoc参数传入applyFilters,而非让do块适配函数类型。
解决方案
修改函数,显式接收Pandoc参数,并在do块末尾将其传入applyFilters:
applyPandocFilters :: (MonadIO m, PandocMonad m) => Pandoc -> m Pandoc applyPandocFilters doc = do luaEngine <- getEngine applyFilters luaEngine Environment{envReaderOptions = readerOptions, envWriterOptions = writerOptions} [ LuaFilter "./filters/lua/filters.lua" , CiteprocFilter ] [] doc -- 传入Pandoc参数
修改后,do块将运行在符合约束的m上下文里:先获取luaEngine,再调用applyFilters并传入所有参数,最终返回m Pandoc,完全匹配函数类型签名。
如果不想使用applyFilters,也可以通过runIO配合applyFilter逐个处理过滤器,但applyFilters是Pandoc官方推荐的批量处理方式,上述修改即可解决问题。
内容的提问来源于stack exchange,提问作者paid50-face-pretty
相关产品推荐
相关产品推荐

