问题
I tried to validate the construction of a Record
with Applicatives
and the Either Monad
. It works fine. But I can't see all Error Messages. Only the first is visible because the Right
Path of the Either Monad
ignores them.
Here is my code:
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'
My question is how can I keep and display all error messages. Maybe with the State Monad?
回答1:
That's because of the way the Either
Applicative
instance works. What you can do is to wrap Either
in a newtype
:
newtype Validation e r = Validation (Either e r) deriving (Eq, Show, Functor)
Then give it another Applicative
instance:
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)
You can now use <$>
and <*>
to compose a Validation [Err] Record
result. See my article on Applicative validation for more details.
回答2:
To accumulate errors, you need a different Applicative
instance for Either
. This variant of Either
is sometimes called Validation
. At least two libraries on Hackage provide a variant of Either
with that instance:
- 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)
On this topic, a common point of contention is whether the "validation" variant is compatible with the Monad
operations of Either
(or whether it should be compatible in the first place):
(u <*> v) = (u >>= \f -> v >>= \x -> pure (f x))
I mentioned two libraries above because there are differing opinions on the topic (which I think boil down to there being no agreed upon conventional definition of equality, which itself is a symptom of Haskell having no formal semantics).
- The validation library says that no compatible monad instance exists, so refrains from defining one.
- The monad-validate library considers that the law above holds up to a particular notion of equivalence, which is arguably okay to do in the context of error reporting, where the worst that should happen is that you might report fewer error than you'd expect. (The library's documentation also contains a lot of relevant exposition.)
来源:https://stackoverflow.com/questions/63346279/how-is-it-possible-to-collect-all-error-messages-in-the-either-monad