Exception Haskell中的非一元错误处理?

Exception Haskell中的非一元错误处理?,exception,haskell,error-handling,monads,applicative,Exception,Haskell,Error Handling,Monads,Applicative,我想知道是否有一种优雅的方法可以在Haskell中执行非单元错误处理,这种方法在语法上比使用普通的或者更简单。我想处理的是非IO异常,例如在解析中,您自己生成异常,以便在稍后的时候让自己知道,例如,输入字符串中有错误 我问的原因是,单子对我来说似乎是病毒。如果我想使用异常或类似异常的机制来报告纯函数中的非关键错误,我总是可以使用或并对结果进行案例分析。一旦我使用了单子,提取单子值的内容并将其提供给不使用单子值的函数就很麻烦/不容易了 一个更深层次的原因是,对于许多错误处理来说,monad似乎是一

我想知道是否有一种优雅的方法可以在Haskell中执行非单元错误处理,这种方法在语法上比使用普通的
或者
更简单。我想处理的是非IO异常,例如在解析中,您自己生成异常,以便在稍后的时候让自己知道,例如,输入字符串中有错误

我问的原因是,单子对我来说似乎是病毒。如果我想使用异常或类似异常的机制来报告纯函数中的非关键错误,我总是可以使用
并对结果进行
案例
分析。一旦我使用了单子,提取单子值的内容并将其提供给不使用单子值的函数就很麻烦/不容易了

一个更深层次的原因是,对于许多错误处理来说,monad似乎是一种过度杀伤力。据我所知,使用monad的一个基本原理是monad允许我们遍历一个状态。但是在报告错误的情况下,我不认为需要线程状态(除了故障状态,我真的不知道使用monad是否必要)

(

编辑:正如我刚才所读到的,在monad中,每个操作都可以利用以前操作的结果。但是在报告错误时,通常不需要知道以前操作的结果。因此,使用monad可能会造成过度杀伤力。在许多情况下,只需在不知道错误的情况下中止并在现场报告失败y先前状态。
Applicative
对我来说似乎是一个限制较少的选择

在具体的解析示例中,我们提出的执行/错误在本质上是否真的有效?如果不是,是否有比
Applicative
更弱的错误处理模型

)

那么,是否有一种比单子更弱/更通用的范式可以用来模拟错误报告?我现在正在阅读Applicative,并试图找出它是否合适。只是想事先问一下,这样我就不会错过显而易见的事情

与此相关的一个问题是,是否有一种机制可以简单地用
字符串将每个基本类型括起来。我在这里问的原因是,所有的单子(或者可能是函子)都用类型构造函数封装一个基本类型。因此,如果您想将非异常感知函数更改为异常感知函数,您可以从,例如

f:: a -> a   -- non-exception-aware

但是,这种变化打破了原本在非例外情况下有效的功能组合。当你能做的时候

f (f x)
你做不到

f' (f' x)
因为有围墙。解决可组合性问题的一种可能比较简单的方法是将
f
更改为:

f'' :: m a -> m a
我想知道是否有一种优雅的方法可以让错误处理/报告在这方面发挥作用

谢谢

--编辑---

为了澄清这个问题,举一个例子,制作一个简单的函数

  g' i j k = i / k + j / k
能够处理零误差除法,当前的方法是按表达式项分解,并在一元操作中计算每个项(有点像在汇编语言中重写):

g'::Int->Int->Int->任一算术错误Int
g'i j k=
做q1
我问的原因是,单子对我来说似乎是病毒

这种病毒性特征实际上非常适合于异常处理,因为它迫使您认识到您的函数可能会失败,并处理失败案例

一旦我使用单子,提取单子的内容就很麻烦/不容易 一个一元值,并将其提供给不使用一元值的函数

您不必提取值。以
Maybe
作为一个简单的例子,通常您可以编写简单的函数来处理成功案例,然后使用
fmap
将它们应用于
Maybe
值和
Maybe
fromMaybe
来处理失败并消除
Maybe
包装
可能是一个单子,但这并不要求您一直使用单子界面或
do
符号。一般来说,“一元”和“纯”之间没有真正的对立

据我所知,使用单子的一个理由是单子允许我们 穿过一个州

这只是许多用例中的一个。
Maybe
monad允许您在失败后跳过绑定链中的任何剩余计算。它不执行任何类型的状态

那么,是否有一种比单子更弱/更普遍的范式可以 用于建模错误报告?我现在正在阅读
Applicative
并尝试 看看是否合适

您当然可以使用
Applicative
实例链接
计算
(*>)
相当于
(>>)
,没有与
(>>=)
等价的东西,因为
应用程序的功能不如
Monad
。虽然通常情况下,不使用超出实际需要的电源是一件好事,但我不确定使用
Applicative
是否更简单

虽然你可以做
f(fx)
但你不能做
f'(f'x)


您可以编写
f'在您的第一个示例中,您希望自己编写一个函数
f::a->ma
。为了便于讨论,让我们选择一个特定的
a
m
Int->Maybe Int

编写可能有错误的函数 好的,正如你所指出的,你不能只做
f(fx)
。好吧,让我们把这一点进一步推广到
g(fx)
(假设我们得到了一个
g::Int->可能是String
,以使事情更具体)并看看您需要逐案做什么:

f :: Int -> Maybe Int
f = ...

g :: Int -> Maybe String
g = ...

gComposeF :: Int -> Maybe String
gComposeF x =
  case f x of           -- The f call on the inside
    Nothing -> Nothing
    Just x' -> g x'     -- The g call on the outside
这有点冗长,正如你所说,我们希望减少重复。我们还可以注意到一种模式:
Nothing
总是转到
Nothing
,并且
x'
被接受
  g' :: Int -> Int -> Int -> Either ArithmeticError Int
  g' i j k = 
    do q1 <- i `safe_divide` k
       q2 <- j `safe_divide` k
       return (q1 + q2)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
f :: Int -> Maybe Int
f = ...

g :: Int -> Maybe String
g = ...

gComposeF :: Int -> Maybe String
gComposeF x =
  case f x of           -- The f call on the inside
    Nothing -> Nothing
    Just x' -> g x'     -- The g call on the outside
bindMaybe :: Maybe Int -> (Int -> Maybe String) -> Maybe String
bindMaybe Nothing   g = Nothing
bindMaybe (Just x') g = g x'
gComposeF :: Int -> Maybe String
gComposeF x = bindMaybe (f x) g
bindMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b
bindMaybe Nothing   g = Nothing
bindMaybe (Just x') g = g x'
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
instance Monad Maybe where
  return x      = Just x
  Nothing >>= f = Nothing
  Just x  >>= f = f x
gComposeF x = f x >>= g
gComposeF x = g =<< f x
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c

-- Specialized to Maybe, we get:
(<=<) :: (b -> Maybe c) -> (a -> Maybe b) -> a -> Maybe c
gComposeF = g <=< f  -- This is very similar to g . f, which is how we "normally" compose functions
multiplyByTen :: Int -> Int
multiplyByTen x = x * 10

maybeCount :: String -> Maybe Int
maybeCount = ...

countThenMultiply :: String -> Maybe Int
countThenMultiply str =
  case maybeCount str of
    Nothing -> Nothing
    Just x  -> multiplyByTen x
overMaybe :: (Int -> Int) -> Maybe Int -> Maybe Int
overMaybe f mstr =
  case mstr of
    Nothing -> Nothing
    Just x  -> f x
overMaybe :: (a -> b) -> Maybe a -> Maybe b
countThenMultiply str = overMaybe multiplyByTen (maybeCount str)
fmap :: Functor f => (a -> b) -> f a -> f b

-- Specializing f to Maybe:
fmap :: (a -> b) -> Maybe a -> Maybe b
countThenMultiply str = multiplyByTen <$> maybeCount str
g' i j k = i / k + j / k
g' i j k = (safeDivide i k) + (safeDivide j k)
fmap (+) (safeDivide i k)    :: Either ArithmeticError (Int -> Int)
eitherApply :: Either ArithmeticError (Int -> Int) -> Either ArithmeticError Int -> Either ArithmeticError Int
eitherApply ef ex =
  case ef of
    Left err -> Left err
    Right f  ->
      case ex of
        Left err' -> Left err'
        Right x   -> Right (f x)
g' i j k = eitherApply (fmap (+) (safeDivide i k)) (safeDivide j k)
g' i j k = (<*>) (fmap (+) (safeDivide i k)) (safeDivide j k)

-- This is the same as
g' i j k = fmap (+) (safeDivide i k) <*> safeDivide j k
g' i j k = (+) <$> safeDivide i k <*> safeDivide j k
firstString, secondString :: String

result :: Int
result = f firstString secondString
firstString', secondString' :: Maybe String

result :: Maybe Int
result = f <$> firstString' <*> secondString'
-- When we apply normal values (x :: A, y :: B, z :: C, w :: D):
result :: E
result = f x y z w

-- When we apply values that have an Applicative instance, for example x' :: Maybe A, y' :: Maybe B, z' :: Maybe C, w' :: Maybe D:
result' :: Maybe E
result' = f <$> x' <*> y' <*> z' <*> w'