ref: edd0699416df633c8199ecfecb48f5f18917a32d
parent: 6e86aec813afcb79caf42dc5c01a422c60fa50f3
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jan 29 18:40:40 EST 2017
Clarify and improve explanation of pattern matching.
--- a/doc/lang.txt
+++ b/doc/lang.txt
@@ -1473,57 +1473,160 @@
matchstmt: "match" expr "\n" matchpat* ";;"
matchpat: "|" pat ":" blockbody
+ pat: expr
- Match statements do pattern matching on values. They take as an
- argument a value of type 't', and match it against a list of other
- values of the same type. The patterns matched against can also contain
- free names, which will be bound to the sub-value matched against. The
- patterns are checked in order, and the first matching pattern has its
- body executed, after which no other patterns will be matched. This
- implies that if you have specific patterns mixed with by more general
- ones, the specific patterns must come first.
+ Match statements perform deep pattern matching on values. They take as
+ an argument a value of type 't', and match it against a list of other
+ values of the same type.
+
+ The patterns matched against may free variables, which will be bound
+ to the sub-value matched against. The patterns are checked in order,
+ and the first matching pattern has its body executed, after which no
+ other patterns will be matched. This implies that if you have specific
+ patterns mixed with by more general ones, the specific patterns must
+ come first.
+ All potential cases must be covered exhaustively. Non-exhaustive
+ matches are a compilation error.
+
Match patterns can be one of the following:
+ - Wildcard patterns
+ - Gap patterns
+ - Atomic literal patterns
+ - String patterns
- Union patterns
+ - Tuple patterns
+ - Struct patterns
+ - Array patterns
+ - Constant patterns
+ - Pointer chasing patterns
- These look like union constructors, only they define
- a value to match against.
+ 6.3.1. Wildcards and Gaps:
- - Literal patterns
+ Wildcard patterns an identifier that is not currently in scope.
+ This variable name captures the variable. That is, in the body of
+ the match, there will be a variable in scope with the same name as
+ the identifier, and it will contain a copy of the value that is
+ being matched against. A wildcard pattern always matches
+ successfully.
- Any literal value can be matched against.
+ Gap patterns are identical to wildcard patterns, but they do not
+ capture a copy of the value being matched against.
- - Constant patterns
+ 6.3.2. Literal and Constant Patterns:
- Any constant value can be matched against.
+ Most pattern matches types are literal patterns. These are simply
+ written out as a literal value of the type that is being matched
+ against.
- More types of pattern to match will be added over time.
+ Atomic literal patterns match on a literal value. The pattern is
+ compared to the value using semantics equivalent to the `==`
+ operator. If the `==` operator would return true, the match is
+ successful.
- Match statements consist of the keyword 'match', followed by
- the expression to match against the patterns, followed by a
- newline. The body of the match statement consists of a list
- of pattern clauses. A patterned clause is a '|', followed by
- a pattern, followed by a ':', followed by a block body.
+ String patterns match a byte sequence. The pattern is compared to
+ the value by first comparing the lengths. Then, each byte in the
+ string is compared, in turn, to the byte of the pattern. If the
+ length and all characters are equal, the pattern succeeds.
- An example of the syntax follows:
+ Union patterns compare the union tag of the pattern wtih the union
+ tag on the value. If there is a union body associated with the
+ tag, then the pattern must also have a body. This is recursively
+ matched on. If the tag and the body (if present) both match, this
+ match is considered successful.
+
+ Tuple patterns proceed to recursively check each tuple element for
+ a match. If all elements match, this is a successful match.
- const Val234 = `Val 234 /* set up a constant value */
- var v = `Val 123 /* set up variable to match */
- match v
- /* pattern clauses */
- | `Val 123:
- std.put("Matched literal union pat\n")
- | Val234:
- std.put("Matched const value pat\n")
- | `Val a:
- std.put("Matched pattern with capture\n")
- std.put("Captured value: a = {}\n", a)
- | a
- std.put("A top level bind matches anything.")
- | `Val 111
- std.put("Unreachable block.")
- ;;
+ Struct patterns recursively check each named member that is
+ provided. Not all named members are mandatory. If a named member
+ is omitted, then it is equivalent to matching it against a gap
+ pattern. If all elements match, then this is a successful match.
+
+ Array pattenrs recursively check each member of the array that is
+ provided. The array length must be part of the match. If all array
+ elements match, then this is a successful match.
+
+ Constant patterns use a compile time constant that is in scope for
+ the pattern. The semantics are the same any of the literal
+ patterns listed above.
+
+ 6.3.3. Pointer Chasing Patterns:
+
+ Pointer chasing patterns allow matching on pointer-to-values. They
+ are written with the `&` operator, as though you were taking the
+ address of the pattern being matched against.
+
+ This pattern is matched by dereferencing the value being matched,
+ and recursively matching the value against the pattern being
+ addressed.
+
+ The pointer provided to a pointer chasing match must be a valid
+ pointer. Providing an invalid pointer leads to undefined behavior.
+
+ 6.4.4. Examples:
+
+ 6.4.4.1. Wildcard:
+
+ var e = 123
+ match expr
+ | x: std.put("x = {}\n", x)
+ ;;
+
+ 6.4.4.2. Atomic Literal:
+
+ var e = 123
+ match expr
+ | 666: std.put("wrong branch\n")
+ | 123: std.put("correct match\n")
+ | _: std.put("default branch\n")
+ ;;
+
+ 6.4.4.3. Tuple Literal:
+
+ var e = (123, 999)
+ match expr
+ | (123, 666): std.put("wrong branch\n")
+ | (123, 999): std.put("right branch\n")
+ | _: std.put("default branch\n")
+ ;;
+
+ 6.4.4.3. Union Literal:
+
+ var e = `std.Some 123
+ match expr
+ | `std.Some 888: std.put("wrong branch\n")
+ | `std.Some 123: std.put("right branch\n")
+ | `std.Some x: std.put("other wrong branch\n")
+ | `std.None: std.put("other wrong branch\n")
+ ;;
+
+ 6.4.4.4 Struct Literal:
+
+ type s = struct
+ x : int
+ ;;
+
+ var e : s = [.x=999]
+ match expr
+ | [.x=123]: td.put("wtf, x=123\n")
+ | [.x=x]: std.put("x={}\n", x)
+ ;;
+
+ 6.4.4.5 Pointer Chasing:
+
+ type s = struct
+ x : int#
+ ;;
+
+ var p = 123
+ var e : s = [.x=&p]
+ match expr
+ | [.x=&123]: td.put("good, x=123\n")
+ | [.x=&x]: std.put("wtf, x={}\n", x)
+ ;;
+
6.4. Looping