如何通过List Monad实现自定义语言选择块的非确定性分支选择
问题根因
你参考的示例代码整个experiment的do块运行在[](List Monad)中,<-操作会自动遍历列表中的每个元素;但你自己的代码do块运行在IOThrowsError Monad中,g env sb返回的是IOThrowsError [HStatement],所以x <- g env sb取到的是完整的分支列表,不是单个分支,自然无法传递给接收单个HStatement参数的fstIf。
解决方案
方案1:直接处理列表(最简单,适合不需要全分支非确定性推导的场景)
直接对返回的分支列表做筛选、选分支操作,不需要修改现有monad结构:
fstIf :: HStatement -> Bool fstIf (If (cond, _)) = hval2bool cond -- 可简化原有冗余的if判断 -- 新增工具函数取If分支的执行体 sndIf :: HStatement -> [HStatement] sndIf (If (_, body)) = body h :: Env -> HStatement -> IOThrowsError () h env sb = do allBranches <- g env sb -- 获取所有守卫已经求值完成的If分支 let validBranches = filter fstIf allBranches -- 筛选守卫为真的合法分支 case validBranches of -- 没有合法分支可以抛出自定义错误 [] -> throwError $ StrError "no valid guard in selection block" -- 取第一个合法分支执行,也可以替换为随机选择逻辑实现真正的非确定任选 (target:_) -> mapM_ (evalHStatement env) (sndIf target)
方案2:叠加ListT Monad Transformer(复用List Monad的非确定性语法)
如果你需要和示例一样用do语法表达非确定性逻辑,可以在现有monad栈上叠加ListT transformer,获得List Monad的特性:
import Control.Monad.Trans.List (ListT(..), runListT) import Control.Monad.Trans (lift) import Control.Monad (guard) h :: Env -> HStatement -> ListT IOThrowsError () h env sb = do allBranches <- lift $ g env sb -- 把底层IOThrowsError的操作提升到ListT栈 branch <- ListT $ return allBranches -- 绑定列表,自动遍历每个分支 guard $ fstIf branch -- 过滤不合法分支 lift $ mapM_ (evalHStatement env) (sndIf branch) -- 执行合法分支的body -- 外层调用函数,取第一个执行成功的结果即可 runSelectionBlock :: Env -> HStatement -> IOThrowsError () runSelectionBlock env sel = do execResults <- runListT $ h env sel case execResults of [] -> throwError $ StrError "no valid guard in selection block" _ -> return ()
内容的提问来源于stack exchange,提问作者Mr.Bloom
相关产品推荐
相关产品推荐

