Handling Multiple Errors in Haskell Part Two

Posted on March 18, 2014

In the previous post I discussed how I transitioned from various ways of implementing validation. On the last one involving the combination of the Maybe and State types I said that while I like this one the best so far it still shows a bit more plumbing then I would like. In this post I will show the improved version of this I came to after.

Monad Transformers

My issue from the previous post was that I had to manage the plumbing explicitly when it came to combining my validations into the form validation. A way to resolve this issue is to make use of monad transformers, which give you tools to manage this plumbing.

import Control.Applicative
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Control.Monad.Trans.Maybe

type Age      = Int
type Username = String
data Form     = Form Age Username deriving (Show)

type FormState a = MaybeT (State [String]) a

pushErr :: String -> FormState a
pushErr s = lift (modify (s:)) >> empty

validateAge :: Age -> FormState Age
validateAge age
    | age > 0 = return age
    | otherwise = pushErr "Age needs to be positive"

validateUsername :: Username -> FormState Username
validateUsername username
    | username `elem` usernames = pushErr "Username is already taken"
    | otherwise                 = return username
    usernames = ["foo", "bar", "baz"]

validateForm age username = Form <$> validateAge age
                                 <*> validateUsername username

This code is similar to the previous version, but you’ll notive some slight differences. The main difference is that I used MaybeT instead of Maybe. With that monad transformer I’m able to make a monad with Maybe like features while still using other monads. In this case, the main places this shows up is in the pushErr function and the final form validation function. Now the pushErr function explicitly lifts itself into the State monad to handle errors, and the form validation values can be threaded like how it is done with the Maybe monad.

comments powered by Disqus