如何收集 Either Monad 中的所有错误消息?
How is it possible to collect all error messages in the Either Monad?
我试图用 Applicatives
和 Either Monad
验证 Record
的构造。它工作正常。但是我看不到所有错误消息。只有第一个可见,因为 Either Monad
的 Right
路径忽略了它们。
这是我的代码:
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'
我的问题是如何保留和显示所有错误消息。也许与 State Monad 一起?
这是因为 Either
Applicative
实例的工作方式。您可以做的是将 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
结果。有关详细信息,请参阅我在 Applicative validation 上的文章。
要累积错误,Either
需要一个不同的 Applicative
实例。 Either
的这种变体有时称为 Validation
。 Hackage 上至少有两个库为该实例提供了 Either
的变体:
-- 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)
关于这个话题,一个共同的争论点是“验证”变体是否与Either
的Monad
操作兼容(或者它是否应该首先兼容):
(u <*> v) = (u >>= \f -> v >>= \x -> pure (f x))
我在上面提到了两个库,因为对这个话题有不同的看法(我认为这归结为没有就平等的传统定义达成一致,这本身就是 Haskell 没有正式语义的症状).
- validation 库表示不存在兼容的 monad 实例,因此避免定义一个。
- monad-validate 库认为上述定律符合特定的等价概念,这在错误报告的上下文中可以说是可以的,其中应该发生的最坏情况是您报告的错误可能比您预期的要少。 (图书馆的文档也包含很多相关的论述。)
我试图用 Applicatives
和 Either Monad
验证 Record
的构造。它工作正常。但是我看不到所有错误消息。只有第一个可见,因为 Either Monad
的 Right
路径忽略了它们。
这是我的代码:
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'
我的问题是如何保留和显示所有错误消息。也许与 State Monad 一起?
这是因为 Either
Applicative
实例的工作方式。您可以做的是将 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
结果。有关详细信息,请参阅我在 Applicative validation 上的文章。
要累积错误,Either
需要一个不同的 Applicative
实例。 Either
的这种变体有时称为 Validation
。 Hackage 上至少有两个库为该实例提供了 Either
的变体:
-- 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)
关于这个话题,一个共同的争论点是“验证”变体是否与Either
的Monad
操作兼容(或者它是否应该首先兼容):
(u <*> v) = (u >>= \f -> v >>= \x -> pure (f x))
我在上面提到了两个库,因为对这个话题有不同的看法(我认为这归结为没有就平等的传统定义达成一致,这本身就是 Haskell 没有正式语义的症状).
- validation 库表示不存在兼容的 monad 实例,因此避免定义一个。
- monad-validate 库认为上述定律符合特定的等价概念,这在错误报告的上下文中可以说是可以的,其中应该发生的最坏情况是您报告的错误可能比您预期的要少。 (图书馆的文档也包含很多相关的论述。)