This is part 3 of the miniseries about solving a simple constraint-satisfaction problem:

s e n d + m o r e --------- m o n e yusing monads in C++. Previously: The State Monad

A constraint satisfaction problem may be solved by brute force, by trying all possible substitutions. This may be done using the imperative approach with loops, or a declarative approach with the list monad. The list monad works by fanning out a list of “alternative universes,” one for each individual substitution. It lets us explore the breadth of the solution space. The universes in which the substitution passes all the constraints are then gathered together to form the solution.

We noticed also that, as we are traversing the depth of the solution space, we could avoid testing unnecessary branches by keeping track of some state. Indeed, we start with a list of 10 possibilities for the first character, but there are only 9 possibilities for the second character, 8 for the third, and so on. It makes sense to start with a list of ten digits, and keep removing digits as we make our choices. At every point, the list of remaining choices is the state we have to keep track of.

Again, there is the imperative approach and the functional approach to state. The imperative approach makes the state mutable and the composition of actions requires backtracking. The functional approach uses persistent, immutable data structures, and the composition is done at the level of functions — plans of action that can be executed once the state is available. These plans of action form the state monad.

The functional solution to our problem involves the combination of the list monad and the state monad. Mashing two monads together is not trivial — in Haskell this is done using monad transformers — but here I’ll show you how to do it manually.

## State List

Let’s start with the state, which is the list of digits to choose from:

using State = List<int>;

In the state monad, our basic data structure was a function that took a state and returned a pair: a value and a new state. Now we want our functions to generate, as in quantum mechanics, a whole list of alternative universes, each described by a pair: a value and a state.

Notice that we have to vary both the value and the state within the list. For instance, the function that selects a number from a list should produce a different number *and* a different list in each of the alternative universes. If the choice is, say, 3, the new state will have 3 removed from the list.

Here’s the convenient generic definition of the list of pairs:

template<class A> using PairList = List<pair<A, State>>;

And this is our data structure that represents a non-deterministic plan of action:

template<class A> using StateList = function<PairList<A>(State)>;

Once we have such a non-deterministic plan of action, we can run it. We give it a state and get back a list of results, each paired with a new state:

template<class A> PairList<A> runStateList(StateList<A> st, State s) { return st(s); }

## Composing Non-Deterministic Plans

As we’ve seen before, the essence of every monad is the composition of individual actions. The higher-order function `mbind`

that does the composition has the same structure for every monad. It takes two arguments. One argument is the result of our previous activity. For the list monad, it was a list of values. For the state monad it was a plan of action to produce a value (paired with the leftover state). For the combination state-list monad it will be a plan of action to produce a list of values (each combined with the leftover state).

The second argument to `mbind`

is a continuation. It takes a single value. It’s supposed to be the value that is encapsulated in the first argument. In the case of the list monad, it was one value from the list. In the case of the state monad, it was the value that would be returned by the execution of the plan. In the combined state-list monad, this will be one of the values produced by the plan.

The continuation is supposed to produce the final result, be it a list of values or a plan. In the state-list monad it will be a plan to produce a list of values, each with its own leftover state.

Finally, the result of `mbind`

is the composite: for the list monad it was a list, for the state monad it was a plan, and for the combination, it will be a plan to produce a list of values and states.

We could write the signature of `mbind`

simply as:

template<class A, class B> StateList<B> mbind(StateList<A>, function<StateList<B>(A)>)

but that would prevent the C++ compiler from making automatic type deductions. So instead we parameterize it with the type `A`

and a function type `F`

. That requires some additional footwork to extract the return type from the type of `F`

:

template<class A, class F> auto mbind(StateList<A> g, F k) -> decltype(k(g(State()).front().first))

The implementation of `mbind`

is a combination of what we did for the list monad and for the state monad. To begin with, we are supposed to return a plan, so we have to create a lambda. This lambda takes a state `s`

as an argument. Inside the lambda, given the state, we can run the first argument — the plan. We get a list of pairs. Each pair contains a value and a new state. We want to execute the continuation `k`

for each of these values.

To do that, we run `fmap`

over this list (think `std::transform`

, but less messy). Inside `fmap`

, we disassemble each pair into value and state, and execute the continuation `k`

over the value. The result is a new plan. We run this plan, passing it the new state. The result is a list of pairs: value, state.

Since each item in the list produces a list, the result of `fmap`

is a list of lists. Just like we did in the list monad, we concatenate all these lists into one big list using `concatAll`

.

template<class A, class F> auto mbind(StateList<A> g, F k) -> decltype(k(g(State()).front().first)) { return [g, k](State s) { PairList<A> plst = g(s); // List<PairList<B>> auto lst2 = fmap([k](pair<A, State> const & p) { A a = p.first; State s1 = p.second; auto ka = k(a); auto result = runStateList(ka, s1); return result; }, plst); return concatAll(lst2); }; }

To make state-list into a full-blown monad we need one more ingredient: the function `mreturn`

that turns any value into a trivial plan to produce a singleton list with that value. The state is just piped through:

template<class A> StateList<A> mreturn(A a) { return [a](State s) { // singleton list return PairList<A>(make_pair(a, s)); }; }

So that would be all for the state-list monad, except that in C++ we have to special-case `mbind`

for the type of continuation that takes a void argument (there is no value of type `void`

in C++). We’ll call this version `mthen`

:

template<class A, class F> auto mthen(StateList<A> g, F k) -> decltype(k()) { return [g, k](State s) { PairList<A> plst = g(s); auto lst2 = fmap([k](pair<A, State> const & p) { State s1 = p.second; auto ka = k(); auto result = runStateList(ka, s1); return result; }, plst); return concatAll(lst2); }; }

Notice that, even though the value resulting from running the first argument to `mthen`

is discarded, we still have to run it because we need the modified state. In a sense, we are running it only “for side effects,” although all these functions are pure and have no real side effects. Once again, we can have our purity cake and eat it too, simulating side effects with pure functions.

## Guards

Every time we generate a candidate substitution, we have to check if it satisfies our constraints. That would be easy if we could access the substitution. But we are in a rather peculiar situation: We are writing code that deals with *plans* to produce substitutions. How do you test a plan? Obviously we need to write a plan to do the testing (haven’t we heard that before?).

Here’s the trick: Look at the implementation of `mthen`

(or `mbind`

for that matter). When is the continuation not executed? The continuation is not executed if the list passed to `fmap`

is empty. Not executing the continuation is equivalent to aborting the computation. And what does `mthen`

return in that case? An empty list!

So the way to abort a computation is to pass an argument to `mthen`

that produces an empty list. Such a poison pill of a plan is produced by a function traditionally called `mzero`

.

template<class A> StateList<A> mzero() { return[](State s) { return PairList<A>(); // empty list }; }

In functional programming, a monad that supports this functionality is called a “monad plus” (it also has a function called `mplus`

). It so happens that the list monad and the state-list monad are both *plus*.

Now we need a function that produces a poison pill conditionally, so we can pass the result of this function to `mthen`

. Remember, `mthen`

ignores the individual results, but is sensitive to the size of the list. The function `guard`

, on success, produces a discardable value — here, a `nullptr`

— in a singleton list. This will trigger the execution of the continuation in `mthen`

, while discarding the `nullptr`

. On failure, `guard`

produces `mzero`

, which will abort the computation.

StateList<void*> guard(bool b) { if (b) { return [](State s) { // singleton list with discardable value return List<pair<void*, State>>(make_pair(nullptr, s)); }; } else return mzero<void*>(); // empty list }

## The Client Side

Everything I’ve described so far is reusable code that should be put in a library. Equipped with the state-list monad library, all the client needs to do is to write a few utility functions and combine them into a solution.

Here’s the function that picks a number from a list. Actually, it picks a number from a list in *all possible ways*. It returns a list of all picks paired with the remainders of the list. This function is our non-deterministic selection plan.

template<class A> PairList<A> select(List<A> lst) { if (lst.isEmpty()) return PairList<A>(); A x = lst.front(); List<A> xs = lst.popped_front(); auto result = List<pair<A, State>>(); forEach(select(xs), [x, &result](pair<A, List<A>> const & p) { A y = p.first; List<A> ys = p.second; auto y_xys = make_pair(y, ys.pushed_front(x)); result = result.pushed_front(y_xys); }); return result.pushed_front(make_pair(x, xs)); }

Here’s a little utility function that converts a list (actually, a vector) of digits to a number:

int asNumber(vector<int> const & v) { int acc = 0; for (auto i : v) acc = 10 * acc + i; return acc; }

And here’s the final solution to our puzzle:

StateList<tuple<int, int, int>> solve() { StateList<int> sel = &select<int>; return mbind(sel, [=](int s) { return mbind(sel, [=](int e) { return mbind(sel, [=](int n) { return mbind(sel, [=](int d) { return mbind(sel, [=](int m) { return mbind(sel, [=](int o) { return mbind(sel, [=](int r) { return mbind(sel, [=](int y) { return mthen(guard(s != 0 && m != 0), [=]() { int send = asNumber(vector<int>{s, e, n, d}); int more = asNumber(vector<int>{m, o, r, e}); int money = asNumber(vector<int>{m, o, n, e, y}); return mthen(guard(send + more == money), [=]() { return mreturn(make_tuple(send, more, money)); }); }); });});});});});});});}); }

It starts by creating the basic plan called `sel`

for picking numbers from a list. (The list is the state that will be provided later.) It binds this selection to a very large continuation that produces the final result — the plan to produce triples of numbers.

Here, `sel`

is really a plan to produce a list, but the continuation expects just one number, `s`

. If you look at the implementation of `mbind`

, you’ll see that this continuation is called for every single pick, and the results are aggregated into one list.

The large continuation that is passed to the first `mbind`

is itself implemented using `mbind`

. This one, again, takes the plan `sel`

. But we know that the state on which this `sel`

will operate is one element shorter than the one before it. This second selection is passed to the next continuation, and so on.

Then the pruning begins: There is an `mthen`

that takes a `guard`

that aborts all computations where either `s`

or `m`

is zero. Then three numbers are formed from the selected digits. Notice that all those digits are captured by the enclosing lambdas by value (the `[=]`

clauses). Yet another `mthen`

takes a `guard`

that checks the arithmetics and, if that one passes, the final plan to produce a singleton triple `(send, more, money)`

is returned.

In theory, all these singletons would be concatenated on the way up to produce a list of solutions, but in reality this puzzle has only one solution. To get this solution, you run the plan with the list of digits:

List<int> lst{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; cout << evalStateList(solve(), lst);

The function `evalStateList`

works the same way as `runStateList`

except that it discards the leftover state. You can either implement it or use `runStateList`

instead.

## What’s Next?

One of the advantages of functional programming (other than thread safety) is that it makes refactoring really easy. Did you notice that 8 lines of code in our solution are almost identical? That’s outrageous! We have to do something about it. And while we’re at it, isn’t it obvious that a substitution should be represented as a map from characters to digits? I’ll show you how to do it in the next installment.

All this code and more is available on github.

## Leave a Reply