I have a Haskell function which takes user input and another function which validates this input. Of course, the validation could fail, in which case I would like to return an error message giving some feedback on what was done incorrectly.
I know that there are many ways that I could do this. After the little experience I have, it seems like the best way is to use Either String a
. What is throwing me off is that I don't care about the a
. Either it fails and I would like to store more information, or it succeeds. The a
is wasted.
Is using Maybe String
an acceptable way to store an error message? It feels backwards to me, but completely ignoring the value in the right of an Either
feels pretty bad too. What is canonical here?
I encourage the use of Except String ()
(or Either String ()
) over Maybe String
, for a few reasons:
String
is a phone number, you might want to return the area code, first, and second parts of the number, giving a validation type like String -> Except String (Int, Int, Int)
or similar. Making validators that don't return anything interesting have type Foo -> Except String ()
makes them just a special case of this pattern -- and therefore easier to fit together.Continuing the "fit together" part, you may later find that you want to construct one big validator out of smaller ones. Perhaps you have a validator that checks that a person has specified a valid age and birth date, and want to build a validator out of this that also checks that the age is about right for the birth date. The Monad
instance for Either
will help here; for example:
validatePerson p now = do
age <- validateAge p
date <- validateBirthdate p
validateMatchingAgeAndDate age date now
Or perhaps there are two ways for some value to validate properly and you want to allow either. Then bigValidator v = option1 v <|> option2 v
is a cheap and cheerful way to combine the two ways of validating.
As a side benefit, these methods of combining existing validators to make bigger ones will be instantly recognizable to other Haskellers.
There is a very strong convention that Nothing
is a failure. Using the opposite convention is not a problem, necessarily, but could be confusing to other contributors and potentially to yourself far in the future.
What's an error message and what's an expected value is just a matter of you point of view. If you don't care about the a
result value but do care about a possible error message, then that message is, as far as you're concerned, the value of interest. So, sure you can store it as a Maybe String
.
In fact it's little different with Either
. What I find backwards is that Either
is usually perceived as “the possible-failure type”. Yes, its monad instance happens to work in the way that makes Left
error-ish and Right
success-ish, but ab initio Either
is just a simple bifunctor expressing the sum of two types. Indeed, if Haskell had always had type operators, Either a b
would probably be written a + b
or a || b
.
If you had an Either String a
and want to “confiscate” the possible a
value, the easiest way is to just fmap (const ())
over it, resulting in an Either String ()
, which is isomorphic to Maybe String
but “looks more like the String
has error character”, though as I said this is a bit silly.
To make it clear from the types that you're talking about error messages, I would use neither Either
nor Maybe
but Except String ()
. Often, error values are caught over some other monad anyway, so you'd have e.g. ExceptT String IO ()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With