I know it's outside the scope of this post, but I'd like to add that F# adds a really nice feature called Active Patterns which are incredibly handy when your expression tree and logic become more complicated.
In the author's example, he matches on a few rules. A zero rule, an identity rule, an evaluation rule, and the default like so:
let simplify1 e =
match e with
| Add (Const 0, x)
| Add (x, Const 0)
| Mul (x, Const 1)
| Mul (Const 1, x) -> x
| Mul (x, Const 0)
| Mul (Const 0, x) -> Const 0
| Add (Const a, Const b) -> Const (a + b)
| Mul (Const a, Const b) -> Const (a * b)
| _ -> e
Active Patterns let you define these patterns as reusable blocks like so: let (|IdentityExpr|_|) = function
| Add (Const 0, x)
| Add (x, Const 0)
| Mul (x, Const 1)
| Mul (Const 1, x) -> Some(x)
| x -> None
It has the effect of making your pattern matching code more readable with a syntax that somewhat resembles a Prolog horn clause. As a bonus, you can mix and match these patterns when doing advanced reasoning. let simplify1 e =
match e with
| IdentityExpr(x) -> x
| ZeroExpr() -> 0
| ConstantExpr(expr) -> Const(eval expr)
| _ -> e