Haskell将不确定性与错误处理相结合

如何解决Haskell将不确定性与错误处理相结合

假设我正在创建一个简单的解释器,该解释器可能会引发错误,例如

type Error = String

data Term = Con Int | Div Term Term

eval :: (MonadError Error m) => Term -> m Int
eval (Con a) = return a
eval (Div u v) = do
  a <- eval u
  b <- eval v
  if b == 0 then
    throwError "Division by zero"
  else
    return $ a `div` b

具体错误处理程序的典型选择是Either Error

runEval :: Term -> Either Error Int
runEval = eval

现在假设我要扩展此解释器以处理非确定性。例如,我可以添加一个术语Choice Term Term,它可以求值第一个或第二个参数。

data Term = Con Int | Div Term Term | Choice Term Term

然后我可以将具体评估表示为[Either Error Int],其中列表中的每个项目代表一个可能的评估。但是,我在如何在不修改Choiceeval情况下将Con情况添加Div函数中的问题中。

我尝试过的事情:

eval :: (MonadError Error m,Monadplus m) => Term -> m Int
-- The Con and Div cases remain unchanged.
eval (Choice u v) = do
  w <- return u `mplus` return v  -- pick either u or v
  eval w

runEval :: Term -> [Either Error Int]
runEval = runExceptT . eval

但是,这只会返回第一个可能的结果,而不会回溯。

> let t = Con 1 `Div` (Choice (Con 0) (Con 1))
> runEval t
[Left "Division by zero"]

我期望的是:[Left "Division by zero",Right 1]

结合非确定性和错误处理的正确方法是什么?

解决方法

问题的根源是ExceptTMonadPlus实例。

(Monad m,Monoid e) => MonadPlus (ExceptT e m)

它不会搭载在基本monad MonadPlus的{​​{1}}实例上。取而代之的是,它需要错误m中的Monoid实例。

并且e不会返回所有失败和成功的集合。取而代之的是,它返回 first 成功或所有失败的简单组合:

mplus

我们可以尝试定义自己的单子,并拥有所需的ghci> throwError ['a'] `mplus` throwError ['b'] :: Except String () ExceptT (Identity (Left "ab")) ghci> throwError ['a'] `mplus` throwError ['b'] `mplus` return () :: Except String () ExceptT (Identity (Right ())) ghci> return 'a' `mplus` return 'b' :: ExceptT () [] Char ExceptT [Right 'a'] 实例(同时重用MonadPlusExceptT的所有其他实例,以避免重复样板。)

deriving

现在,它似乎可以按预期工作:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Except

newtype OopsT e m a = OopsT { runOopsT :: ExceptT e m a }
    deriving stock (Show)
    deriving newtype (Show,Functor,Applicative,Monad,MonadError e,MonadTrans)

-- We delegate on the Alternative/Monadplus instance of the base monad m
instance MonadPlus m => Alternative (OopsT e m) where
    empty = OopsT (ExceptT empty)       
    OopsT (ExceptT xs) <|> OopsT (ExceptT ys) = OopsT (ExceptT (xs <|> ys)

instance MonadPlus m => MonadPlus (OopsT e m) where
    mzero = empty       
    mplus = (<|>)

runEval :: Term -> [Either Error Int]
runEval = runExceptT . runOopsT . eval

ghci> let t = Con 1 `Div` (Choice (Con 0) (Con 1)) ghci> runEval t [Left "Division by zero",Right 1] 的{​​{1}}实例的一个可能令人不安的方面是,它似乎不符合documentation中提到的MonadPlus律。例如:

OopsT

也许我们可以使用等效的v >> mzero = mzero实例,但这似乎并不需要该法律?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?