I recently started a toy Haskell project implementing some concepts

related to Event Sourcing and CQRS. I find strong the abstractions and

type system from Haskell usually help in solidifing my understanding

of most concepts.

This post will start things off only tangentially related to CQRS and will

describe a small DSL I’ve put together for modelling validation of

commands.

We’ll end up being able to compose commands from smaller parts, while

still being able to run validations against them in a “transactional” way,

meaning the validation will run before any side-effects occur, and if there

is a failure, all side-effects will be prevented from happening. Essentially,

a unit of work type behavior that arises through correctness of construction.

This will be a win, as composability is always a critical feature of any

well designed software system. It will also lead us to a system that will

be more expressive than something like attributes/annotations in C# or Java,

while keeping our code simple such that it will interact well with our

other abstractions.

We will for now skip modelling things like using a read/write model

and use commands that are simple IO actions, such as

addUserCommand :: String -> IO () addUserCommand user = printf "Added user %sn" user setPasswordCommand :: String -> String -> IO () setPasswordCommand user pwd = printf "Set password for user %s to '%s'n" user pwd

I’ll mostly be interested in the side-effects themselves, which are

exemplified as simple print statements.

Now for modelling validations themselves, there are two things I’d

like a validation type to satisfy.

There should be an “empty” validation, that never fails.

There should be a way to compose validations into one validation

object, such that it encompasses it’s parts and collects errors from

both.

Naturally, this suggests using a Monoid . We’ll just use a list in

this example, but I will abstract over monoids, as it will lead to

more flexible code, and will keep me honest about how the API is used.

Now there needs to be a way to combine commands with their validations,

such that we can compose commands while still ensuring that we can run

all validation logic before any side-effects occur.

Of course, Haskell’s standard set of abstractions contains the answer

we need in order to maximize the expressiveness for this API. We want

to combine effectful objects such that the resulting “control flow”

can be predicted ahead of running effects. The interface we need is

Applicative , for applicative functors, a generalization of the

Monad class.

Applicative functors as a generalization of monads, are thus also

somewhat less powerful. This loss of power is however exactly what is

need to achieve our goals.

We define a data type Attributed that contains an effectful object

( m ) running side-by-side with a monoid ( w ).

data Attributed m w a = Attributed (m a) w

This datatype cannot be made into a monad in a meaningful way, but can

be made into an applicative in the obvious way.

instance ( Monoid w, Applicative m) => Applicative ( Attributed m w) where pure a = Attributed (pure a) mempty Attributed f v <*> Attributed a w = Attributed (f <*> a) (v <> w)

This code might be hard to understand if you’re not versed in Haskell,

but don’t worry if it looks like opaque, it will not affect the

resulting API, and is really pretty uninteresting. If you want to

understand it, I’d recommend reading up on monoids and applicative

functors.

Before we get to the meat of things we need some helper

functions. We’ll define an operator ( # ) to apply validations to an

effect.

infixr 0 # vs # effect = Attributed effect (mconcat vs)

This will apply a list of validations (monoids) to an effect

(command). Using a list will also have the cute effect of making the

syntax look somewhat like attributes in C#, my paying-the-bills

language of choice.

We also define a simple helper function on lists

assert p err = if p then [] else [err]

and use it to write a function for validating the min-length of

strings.

minLength name n str = assert (length str >= n) $ printf "%s %s: Min-length is %d" name str n

Note that printf is polymorphic, and above becomes a pure function

to strings, while in the commands it’s side-effecting and prints to

console. (Type system win).

We now can define some commands with validation.

addUserCommand :: String -> Command () addUserCommand user = [ minLength "Username" 5 user ] # printf "Added user %sn" user setPasswordCommand :: String -> String -> Command () setPasswordCommand user pwd = [ minLength "Password" 7 pwd ] # printf "Set password for user %s to '%s'n" user pwd

Above I’ve defined the type Command as an attributed effect, where

our monoid is a [String] , or list of strings representing error

messages.

type Command a = Attributed IO [ String ] a

Now, let’s say we want to define a new command that both adds a user

and sets the initial password. We can do this with applicative

composition.

newUserCommand user pwd = addUserCommand user *> setPasswordCommand user pwd

We use a run function to execute commands with their validations.

run :: Command a -> IO () run ( Attributed command w) = case w of [] -> void command errors -> for_ errors $ e -> printf "error: %sn" e

Let’s try it out

*Main> run $ newUserCommand "foo" "password" error: Username foo: Min-length is 5 *Main> run $ newUserCommand "username" "foo" error: Password foo: Min-length is 7 *Main> run $ newUserCommand "username" "password" Added user username Set password for user username to 'password'

Cool. Even if the validation fails for the password, the create user

action is not executed. This could not be the case had we used monadic

composition in something like the similarly constructed writer monad.

To see the flexibility of this abstraction, we’ll do something stupid

like taking a list of usernames and adding all users that start with

the letter ‘s’.

addUsers :: [ String ] -> Command () addUsers users = for_ users $ userName -> case userName of 's' : _ -> addUser userName _ -> pure () -- do nothing

Let’s test it

*Main> run $ addUsers ["simon_marlow", "spj", "pni", "conal", "simon_thompson"] error: Username spj: Min-length is 5 *Main> run $ addUsers ["simon_marlow", "pni", "conal", "simon_thompson"] Added user simon_marlow Added user simon_thompson

Sweet. This example is contrived, but illustrates we can combine

commands using arbitrary “control-flow” such as “for-loops” and

“if-statemens” (or the haskell versions thereof), as long as we don’t have any dependencies on the side-effects themselves, which makes a lot of sense.

The occurence of invalid username “pni” does not fail the computation

as it is never inserted, and even if we use the for control function

we are still “transactional” over the side effects, so this acheives

what we want.

From here we can extend this abstraction with an explicit read model,

that will be available to validate against, while ensuring no invalid

actions are taking against the write model.

Running without validation

We can of course also execute commands without their validations, simply

by ignoring any attached validation. Since Haskell is lazy by default,

this will come at essentially no runtime cost either.

run_unsafe (Attributed m w) = m

Conclusion

It’s amazing what can be achieved with only a few lines of Haskell, when

we chose abstractions wisely. Check out the full source here.

I find the ability to design programs such as these a big win for

languages like Haskell, and for strong type checking and in particular

the separation of pure and effectful computation via the type system,

as well as design patterns such as monoids and applicatives. These go

a long way of ensuring our programs are correctly constructed, and

lead us towards simple yet expressive code. Perhaps now we can put to rest

ideas such as type systems being the root of all evil.