

> {-# LANGUAGE GADTs #-}





n

n

3

4

5



0(a+b) = 0a

1(a+b) = 1b





abc = bc (here a and b are each either 0 or 1)



a+(b+c) = a+c = (a+b)+c



a+a = a = 0a+1a





f(n)

= 0x n if n is even

= 1x n-1 if n is odd





f*u = u with x n replaced by f(n) throughout



3

4

5

n

n

n



η:N→TN defined by η(n)=x n

and

μ:T(T(N))→T(N) defined by μ(a)=i*a, where i is the identity.



>>=

n

s



> data Expr s n where





Bool

Bool

s



> Put :: s -> Expr s n -> Expr s n





False

True

`Put`

Get



Get :: Expr s n -> Expr s n -> Expr s n





Get :: (Expr s n, Expr s n) -> Expr s n



(a, a)

Bool -> a



> Get :: (s -> Expr s n) -> Expr s n





Get

s = Bool



> Var :: n -> Expr s n







> fold :: (s -> w -> w) -> ((s -> w) -> w) -> (n -> w) -> Expr s n -> w

> fold put get var (Put s x) = put s (fold put get var x)

> fold put get var (Get c) = get (\s -> fold put get var (c s))

> fold put get var (Var n) = var n







> instance Monad (Expr s) where

> return n = Var n

> x >>= f = fold Put Get f x where





Get

Get

Put

Put

Var n

f n

State

State

Expr

State

s

n

((s, n), (s, n))

s -> (n, s)



> canonical :: Expr s n -> (s -> (n, s))

> canonical = fold put get var where





n

n

n



> var n = \s -> (n, s)





s -> (n, s)



> put s c = \t -> c s







> get c s = c s s





canonical



> test1 =

> Get $ \c -> if c

> then Put False $

> Get $ \d -> if d

> then Var 5

> else Var 4

> else

> Put True $

> Var 3

> go1 = canonical test1 False





canonical



> inject :: (s -> (n,s)) -> Expr s n

> inject f = Get $ (\(n, s) -> Put s (Var n)) . f





inject . canonical

State s n

Expr s n

State

Monad



> data State s n = State { runState :: s -> (n, s) }



> instance Monad (State s) where

> return n = State $ canonical $ Var n

> State f >>= g = State $ canonical $ inject f >>= (inject . runState . g)





State

Monad

Put

Get



> get = State $ canonical (Get (\s -> Var s))

> put n = State $ canonical $ Put n (Var ())



> test2 = do

> c <- get

> if c

> then do

> put False

> d <- get

> if d

> then return 5

> else return 4

> else do

> put True

> return 3



> go2 = runState test2 False





canonical

Monads play an important part in implementing computational "effects" in Haskell, and yet monads come out of pure mathematics where they are used to describe operations in algebraic structures. What do these two applications have to do with each other? In this post I hope to answer that question and maybe give another view on monads.You might not have seen one of these in a long time. A flowchart describing a subroutine:For now I'm considering flowcharts only with nodes to set or reset just one flag, test the flag, and return values. I'm not allowing any paths to reconverge. So these flowcharts are trees describing the possible paths of control flow.We can describe such flowcharts with a more compact notation. Use xto mean:Think of xbeing an variable of unknown value.Use the + operator to mean tests of the flag. So x+y represents:Note that this '+' isn't commutative because the left branch will always mean 'no' and the right branch will mean 'yes'.And we'll use the symbols '0' and '1', applied on the left, to mean these two nodes respectively:The flowchart above can now be written compactly as 1x+0(x+x). (Note that I'm overloading the notation for addition and multiplication, this isn't intended to be ordinary addition and multiplication of numbers.)It's clear that the 'return 5' never gets executed. So the above flowchart is equivalent to this one:The rule describing this simplification can be expressed with our compact notation as the rule:So we already have a strong clue that there is a connection between algebra and computation. Here are some more rules that you can check for yourself:Now suppose we wish to extend this subroutine so it does a bit more work. Instead of simply returning n we might want it to set a flag according to whether the previous result is even or odd, and subtract 1 if it is odd. It's a little awkward because I've insisted that these trees don't rejoin so we need to patch the tree in three places using the rule:Note that the descriptions of the two cases and the n here aren't part of the flowchart, they're part of the language I'm using to talk about flowcharts.We can do something similar for our algebraic expressions. We can define a mathematical function taking integers to algebraic expressions such thatand now define an operation * byOur updated flowchart can now be represented by f*(1x+0(x+x)). Now you might see why I used the weird xnotation. The * operation is ordinary substitution of an expression for a variable that we learn in high school, so I used xnotation to make it clear that xis a variable. You can think of f as a dictionary saying how the values of the variables are to be expanded.Let TN be the set of expressions in our algebra where the subscripts of the x's come from the set N. Then we have the functions:We have a monad . (Exercise: check the monad laws.) Note that this definition isn't specific to our particular algebra. This definition applies to a very broad class of algebraic expression. Monads in mathematics are used to describe substitution of subexpressions into expressions. But I hope you can now see why they have a role to play in computing: the act of substituting an expression for a variable is essentially the same thing as grafting more operations on to the end of our tree of computations.But if you look at the definitions of typical monads in the Haskell libraries, they don't look much like trees, and the implementation ofdoesn't look much like grafting trees. The rest of this post will be about working through a specific example to show that it really is about tree grafting after all.We'll need to define a type to represent our algebraic expressions in Haskell. Theis going to be used to label our variables, andwill be explained shortly:Now we need constructors to represent 0 and 1. We can simplify this by combining them into one constructor and using the typeto signify whether it is 0 or 1 that we want. But there's no need to restrict ourselves toso I'll make the code more general and useinstead:So 0 isand 1 isandis our multiplication operator. (Maybe you've already guessed why I called it that.)The next constructor will be for addition. I'm going to give it the weird name. The obvious thing to write would be:We could uncurry that instead and useBut we know that a pairis the same as a function. So I'm going to actually use:So instead of giving us a binary node in a tree,gives us a possible infinite s-ary node. But we'll be working withfor now.Now we need variables. We'll just use:This is a recursive type so we can write a fold for it. If we'd been using generic library we'd get the fold for free but I'm giving it explicitly:We can implement the monad for substitution:Note how the fold recursively replaceswithwithandwithBy now the game's up. If you read my post from last week then you know that I'm building something like themonad. But although this monad has a similar interface, internally it's nothing like. For one thing,is a tree type and internally it could end up with an arbitrarily large number of nodes. How can we extract the familarmonad from this?Expressions in various algebraic languages can often be reduced to a canonical form. For example, we have disjunctive normal form for (a fragment of) logic. Expressions in our language above can be reduced to a canonical form. In the language of flowcharts, every one of our flowcharts can be reduced to something of the form:Algebraically the claim is that any expression reduces to something of the form au+bv. In the general case the items a and b are of typeand u and v can be represented by elements of. So this is something of type. Again we replace the pair by a function to get the type. So we want a function to convert to canonical form:We can get a return box into canonical form like this:Algebraically this is x→0x+1xand we can implement it asNow we need to simplify the application of 0 or 1 to something already in canonical form. 0(au+bv) = 0au = au and 1(au+bv) = 1bv = bv. Essentially it just picks the first or second term in the sum according to whether we're multiplying on the left by 0 or 1. But that's exactly what the function type indoes. So we can just write:Now we need to consider sums of two terms in canonical form. We can derive this just by looking at the sum of two sums: (p+q)+(r+s). This is just p+s. We get this by picking the left branch twice or the right branch twice, so we're just applying our function twice to the same thing:Comparing with my last post you should see thatis what I called an interpreter. So the act of interpreting these tree expressions is one and the same thing as reducing them to a canonical form. This shouldn't be so surprising - executing a functional program is itself a process of reduction to a normal form. Here's an example of using the above reduction to get our flowchart executed:returns a different tree to the original tree. But we can easily map back to trees again:So the compositiontakes a tree and reduces it to canonical form keeping it as a tree.We can view things like this: thetype is just thetype modulo an equivalence relation. The usualtype is a tree but this fact is obscured because we only work with a representative of each equivalence class, not entire trees. But theinstance is still simply tree grafting. We can prove this as follows:Note how the implementations consist solely of isomorphisms or wrapping/unwrapping. The typeis simply inheriting theinstance from the tree type. We can now putandinto a form usable in the monad. Again, we're just wrapping and "canonicalising" near-trivial operations.Like in the previous post We're starting with signatures for functions. We can form an expression tree type. With the substitution operation this naturally becomes a monad.We also specified equations for our functions. This leads to the idea of a reduced form for our expression trees. The state monad (and, in fact, most of the standard monads) are examples of reduced forms. If we implementfor an algebra, the monad follows for free. More precisely: given a signature and equations, the tree type and monad can be derived automatically. It seems tricky to derive the rules for reducing expressions to canonical form, but once you've done that the monad for the reduced form can be derived automatically.The act of evaluating a monadic expression is the same as reducing an expression tree to its canonical form.Given two algebraic theories with their own signatures and equations they can be combined together. One obvious way is to simply "freely" throw all of the signatures and equations together. This gives the "sum" of the two theories. If we additionally impose some new equations, a certain type of compatibility condition between the theories, then we get the product. So starting with two theories we get the monads for the two theories and the monad for the sum or product. This might provide an alternative approach to monad transformers for combining effects. (Actually, I'm doubtful of this plan for automatically combining effects because surprising things can happen , but I still think there's something useful to be extracted from the theory.)Could this form the basis for more monad tutorials? Programmers from imperative, functional and other backgrounds are familiar with the ideas of trees and substitutions.By the way, it's sometimes useful to implement a monad by building a tree first and then interpreting it separately. That's what I did a few posts ago and I found it much easier than deriving a complete monad in one go.These trees are related to the diagrams I drew here You can repeat much of what I said above with all kinds of algebraic structures. My ICFP talk was on what you can do with the monad that arises from vector spaces and a spent a slide or two on the rules for reduction to canonical form.An earlier post of mine on substitution.You can interpret the canonical form of the expressions above as saying that when you have a bunch of switches (in certain types of code) you can replace them with just one. This is reminiscent of the old folk theorem about loops. In fact, the algebra I describe above is a lot like Kleene Algebra with Tests which can be used to prove that result.This was all motivated by this post on Lambda the Ultimate. Some comments by Derek Elkins (that he's probably long forgotten) and pieces of this by Conor McBride helped me write the code.I originally planned to say more about forming sums and products of theories but the code is going to need more work...Update: Following one of the links I gave above I just found Unimo which is closely related.