Patterns occur in several syntactic contexts in Timber (e.g. in the left hand sides of bindings, lambda expressions and case alternatives). Syntactically, patterns form a subset of expressions; a pattern is one of the following:
At run-time, patterns are matched against values. Pattern-matching may succeed or fail; in the former case the result is a binding of the variables in the pattern to values. The rules are as follows:
The special "wildcard" variable _ may be used in patterns; in contrast to other variables, it is not bound by pattern-matching.
A consequence of the way pattern-matching is done is that patterns must be linear; no variable may occur more than once in a pattern.
Syntactically, bindings are divided into type signatures and equations. Equations are either function bindings, pattern bindings or instance bindings for type classes.
var (, var)* :: qtypeHere a qtype is a (possibly qualified) type.
Examples |
---|
x, y, z :: Int |
map :: (a -> b) -> [a] -> [b] |
elem :: a -> [a] -> Bool \\ Eq a |
We present first the simplest form of function definition, a sequence of equations, and then how these may be extended with where-clauses and with guards; these extensions may be combined.
var pat* = expr
The variable (the name of the function) and the number of patterns must be the same in all equations. The order of equations is significant; when applying the function, pattern-matching is tried starting wih the first equation until it succeeds; the function value is computed from right hand side of that equation, using the variable bindings obtained. Within one equation, patterns are matched from left to right.
Example |
---|
zip (x:xs) (y:ys) = (x,y) : zip xs ys |
zip _ _ = [] |
A binding may have attached a where-clause with a list of local bindings in layout-sensitive syntax
var pat* = expr where bind +
Example |
---|
formatLine n xs = concatMap f xs |
where f x = rJust n (show x) |
var pat* ( | (qual)+ = expr )+
In the most common case, a qual is a Boolean expression. After pattern-matching has succeeded, the guards are evaluated in turn; the right hand side corresponding to the first true guard (if any) is used.
Example |
---|
lookup x [] = Nothing |
lookup x ((a,b) : xs) |
| x == a = Just b |
| True = lookup x xs |
More complicated guards that bind variables are possible, but omitted here.
pat = expr
where pat is not a variable (in which case the binding is, perhaps counter-intuitively, a function binding).
Pattern bindings bind the variables in pat by pattern-matching against the value of expr. A pattern binding may not occur as a top-level declaration, but only as a local binding.
Example |
---|
lookup' x ps = y |
where Just y = lookup x ps |
The pattern-binding in the where-clause will fail (and the function application give a run-time error), if the result of calling lookup is Nothing.
instance var :: type var = exprThe type signature of an instance of a type class is prepended by the keyword instance. For instances, a type signature is compulsory; it is not sufficient to give only the binding defining the instance.
This basic form is to emphasise that an instance is just a struct value that is declared to be used as implicit argument, inserted by the compliler. Two alternative syntactic forms are also available:
instance var :: type = expr
instance var :: type where bind+
With the exception of struct expressions and the second alternative form for instances (which is immediately desugared to a form with a struct expression), bindings in a sequence are mutually recursive. This amounts to bound variables of all types and is independent of whether the corresponding right-hand sides need further evaluation or not.
Evaluation of bindings takes place in dependency order, but follows the declared order for bindings that are truly mutually dependent. This declared order is significant for one specific reason: evaluation of each binding must be able to complete without looking into the actual value of any of its forward references. Failure to do so will result in a run-time error.
Examples | Comments |
---|---|
f 0 = 1 | |
f n = n * f (n-1) | Ordinary recursive function binding |
g h 0 = 1 | |
g h n = n * h (n-1) | Non-recursive higher-order function binding |
f' = g f' | Recursive binding of f' (equivalent to f) |
x = 1 : y | Legal forward reference |
y = 2 : x | Ok, both x and y are cyclic lists |
a = 1 : b | |
b = head a : [] | Legal backward reference (ok to look into the value of a) |
a' = head b' : [] | Illegal forward reference (must look into the value of b') |
b' = 2 : a' | Not reached (run-time error on previous line) |
In addition, binding sequences are subject to the following syntactic restrictions: