如何收集"非此即彼"中的所有错误消息



我试图用ApplicativesEither Monad验证Record的构造。它运行良好。但我看不到所有的错误消息。只有第一个可见,因为Either MonadRight路径忽略了它们。

这是我的代码:

import Data.Either (either)
import Text.Printf (printf)
data Record = Record
{ fieldA :: String
, fieldB :: String
, fieldC :: String
} deriving (Show, Eq)
type Err = String

setField :: String -> String -> Either Err String
setField field value
| length value > 0 = Right value
| otherwise = Left $ printf "value for field %s is to short" field
setFieldA :: String -> Either Err String
setFieldA = setField "fieldA"
setFieldB :: String -> Either Err String
setFieldB = setField "fieldB"
setFieldC :: String -> Either Err String
setFieldC = setField "fieldC"

makeRecord :: Either Err Record
makeRecord = Record
<$> setField "fieldA" "valueA"
<*> setField "fieldB" "valueB"
<*> setField "fieldC" "valueC"
makeRecord' :: Either Err Record
makeRecord' = Record
<$> setFieldA "valueA"
<*> setFieldB "valueB"
<*> setFieldC "valueC"
recordFromEither :: Either Err Record -> Maybe Record
recordFromEither r =
case r of
Right v -> Just $ v
Left _ -> Nothing
main :: IO ()
main = putStrLn $ output
where
output = case makeRecord of
Right v -> show v
Left err -> show err
main' :: IO ()
main' = putStrLn $ either id show makeRecord'

我的问题是如何保存和显示所有错误消息。也许是和国家君主?

这是因为EitherApplicative实例的工作方式。您可以做的是将Either封装在newtype:中

newtype Validation e r = Validation (Either e r) deriving (Eq, Show, Functor)

然后给它另一个Applicative实例:

instance Monoid m => Applicative (Validation m) where
pure = Validation . pure
Validation (Left x) <*> Validation (Left y) = Validation (Left (mappend x y))
Validation f <*> Validation r = Validation (f <*> r)

现在可以使用<$><*>来组成Validation [Err] Record结果。有关更多详细信息,请参阅我关于应用程序验证的文章。

要累积错误,需要为Either提供不同的Applicative实例。Either的这种变体有时被称为Validation。Hackage上至少有两个库提供了Either的变体:

  • https://hackage.haskell.org/package/validation
  • https://hackage.haskell.org/package/monad-validate
-- Standard definition
(<*>) :: Either e (a -> b) -> Either e a -> Either e b
Left e <*> _ = Left e
Right _ <*> Left e = Left e
Right f <*> Right x = Right (f x)
-- "Validation" variant
(<*>) :: Monoid e => Either e (a -> b) -> Either e a -> Either e b
Left e <*> Left e' = Left (e <> e')
Left e <*> Right _ = Left e
Right _ <*> Left e = Left e
Right f <*> Right x = Right (f x)

在这个主题上,一个共同的争论点是";验证";变体与EitherMonad操作兼容(或者它是否应该首先兼容(:

(u <*> v)   =   (u >>= f -> v >>= x -> pure (f x))

我在上面提到了两个库,因为在这个主题上有不同的意见(我认为这可以归结为没有达成一致的平等的传统定义,这本身就是Haskell没有正式语义的症状(。

  • 验证库表示不存在兼容的monad实例,因此避免定义一个
  • monad-validate库认为,上述定律支持一个特定的等价概念,在错误报告的情况下可以这样做,最糟糕的情况是报告的错误可能比预期的要少。(图书馆的文档中也包含了许多相关的阐述。(

最新更新