Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Maybe's be used to hold error messages?

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?

like image 826
qfwfq Avatar asked Apr 05 '17 16:04

qfwfq


2 Answers

I encourage the use of Except String () (or Either String ()) over Maybe String, for a few reasons:

  • You will probably find later that your it is convenient for your validation function to return some parts of the data structure. For example, while validating that a 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.

like image 187
Daniel Wagner Avatar answered Sep 20 '22 21:09

Daniel Wagner


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 ().

like image 38
leftaroundabout Avatar answered Sep 17 '22 21:09

leftaroundabout