Doing stuff with values in the preceding types is easy: we just write clauses in our functions to deal with the various patterns we expect to see.
For example, mirror-reversing a tree—there are two main cases (leaf or node), and each clause says what to do with the contents of the tree.
|||mirror (PLeaf x) = PLeaf x|
|||mirror (PNode l r) = PNode (mirror r) (mirror l)|
Notice that we’re covering all possible cases of tree here. A value which is of tree type is either a leaf or a node, and we provide code to handle both cases in full. We’ll never get a runtime error when an unexpected input is received. Some of the compilers track this “totality” for us, and can give warnings when functions don’t cover all cases. Also, it doesn’t ...