I have this code:
type Variable = String
data Expr = T | Var Variable | And Expr Expr | Not Expr
The test case is as follows:
prop_v1 = v (Not (And (Var "y") T)) === ["y"]
What is the meaning of data Expr – is it just defining the types that can be input and their parameters? I.e. a Var takes a Variable, And takes two Expr‘s.
The datatype declaration for
Exprgives rise, systematically, to a set of patterns which cover all possible things that a value of typeExprcan be. Let’s do the translationYou can see that the
T,Var,AndandNotthat head up eachdataclause are constructors, and live in the value language; the rest of the things in each clause live in the type language, saying what type each component of anExprmust have. Each of the corresponding patterns consists of the constructor applied to pattern variables standing for components which have the given types. Basically, the patterns that show up on the left-hand side of a function are made by repeatedly refining pattern variables to the patterns that their values can possibly take, as indicated by their type.Writing a function by pattern matching does not consist of saying what to do: it consists of saying what the output is for the possible cases of what the input is. You need to analyse the input into cases where you can easily say what the output must be. So, start with one general case…
…and refine it. Ask “Can you tell what it is yet?”. We can’t tell what
v eis without knowing more aboute. So we’d better split e. We know thate :: Expr, so we know what patterns its value can match. Make four copies of your program line, and in each, replaceeby one of the four possible pattern listed above.Now, in each case, can you tell what the output is? The handy thing is that you can make use of recursive calls on components. Assume you already know what
vars e1andvars e2are when you’re trying to say whatv (And e1 e2)must be. If you get the steps right, the program will be correct.I find it’s often helpful to think in terms of concrete examples. Take your test example.
That’s supposed to be
["y"], right? Which pattern does it match?What’s
? Looking at it, it had better be
In this example, what’s
v (Not e1)in terms ofv e1? The very same. That might suggest a suitable expression to replaceundefinedin(Of course, a suggestive example is just a good start, not a guarantee of correctness.)
The takeaway messages: (1) build patterns by splitting pattern variables, figuring out the possible patterns by looking at the declaration of the type; (2) assume that recursive calls on components give you the right answer, then try to construct the right answer for the whole problem.
Shameless plug: shplit is a simple tool I built for my students, capturing message (1) mechanically.