场景:我有一个解释器,可以从AST自下而上构建值。某些节点具有权限 - 其他布尔表达式。权限失败应该传播,但如果AST中的上一个节点带有权限,则成功可以恢复计算并停止传播错误。
起初我认为Error MyError MyValue
monad就足够了:MyError
的其中一个成员可能是PermError
,我可以使用catchError
从{PermError
恢复1}}如果第二次检查成功。但是,当我到达处理程序时,MyValue
已经消失了。我想最终可能有一种方法让PermError携带一个MyValue
字段,以便处理程序可以恢复它,但它可能很难看,并且在每一步检查异常都会破坏的概念例外发生。
我正在考虑另一种抽象方式。基本上我必须返回一个数据类型Either AllErrorsExceptPermError (Maybe PermError, MyValue)
或更简单地(Maybe AllErrors, MyValue)
(其他错误是不可恢复的并且非常适合错误monad)并且我正在寻找可以帮助我摆脱元组周围的元素的东西因为在操作链接的方式上似乎有一个共同的模式。我的haskell知识只到目前为止。在这种情况下,你如何利用haskell获得优势呢?
当我写这篇文章时,我提出了一个想法(SO是一个花哨的橡皮鸭):一个Monad,它在内部处理一个类型(a,b)(当monadic计算终止时,最终返回它,必须有某种runMyMonad
),但让我尽可能直接使用b类型。像
data T = Pass | Fail | Nothing
instance Monad (T , b) where
return v = (Nothing, v)
(Pass, v) >>= g = let (r', v') = g v in (if r' == Fail then Fail else Pass, v')
(Fail, v) >>= g = let (r', v') = g v in (if r' == Pass then Pass else Fail, v')
(Nothing, _) >>= g = error "This should not have been propagated, all chains should start with Pass or Fail"
错误已简化为T,instance
行可能有语法错误,但您应该明白这一点。这有意义吗?
答案 0 :(得分:2)
我认为您可以使用State
monad进行权限和值计算,并将其包装在ErrorT
monad转换器中以处理错误。下面是一个显示这个想法的例子,这里计算总结一个列表,权限是列表中偶数的数量和错误条件是我们在列表中看到0的时候。
import Control.Monad.Error
import Control.Monad.State
data ZeroError = ZeroError String
deriving (Show)
instance Error ZeroError where
fun :: [Int] -> ErrorT ZeroError (State Int) Int
fun [] = return 0
fun (0:xs) = throwError $ ZeroError "Zero found"
fun (x:xs) = do
i <- get
put $ (if even(x) then i+1 else i)
z <- fun xs
return $ x+z
main = f $ runState (runErrorT $ fun [1,2,4,5,10]) 0
where
f (Left e,evens) = putStr $ show e
f (Right r,evens) = putStr $ show (r,evens)