Pattern matching¶
Monte comes with a powerful and extensible subsystem for destructuring and viewing objects. A pattern is a rule which conditionally matches objects and binds parts of the matched objects to names.
pattern
postfixPatt
prefixPatt
namePatt
The Such-That Pattern¶
SuchThatPatt
Non associative.
The such-that pattern contains a subpattern and a condition, not unlike
the condition expression in an if
expression. The such-that pattern first
speculatively performs the pattern match in its subpattern, and then succeeds
or fails based on whether the condition evaluates to true
or false
:
>>> def players := [object alice{}, object bob{}]
...
... object game:
... to vote(player ? (players.contains(player)),
... choice ? (players.contains(choice))) :
... return "voted"
...
... def t1 := game.vote(players[0], players[1])
... def t2 := try { game.vote(object alice{}, "bob") } catch _ { "BZZT!" }
... [t1, t2]
["voted", "BZZT!"]
SuchThat Expansion¶
>>> m`def patt ? (condition) := value`.expand()
m`def via (_suchThat) [patt, via (_suchThat.run(condition)) _] := value`
List, Map Patterns¶
ListPatt
MapPatt
mapPattItem
List patterns match lists, matching each subpattern against the items in the list:
>>> def [x, y] := [5, 10]; x
5
If + rest is used, a list pattern of size N is matched against the first N items in the list, and the rest pattern is matched against the remaining items.:
>>> def [first] + rest := [1, 2, 3, 4]
... rest
[2, 3, 4]
If +
is not used, the list pattern only matches lists of the same size
Map patterns match maps. Keys are either literal strings or expressions in parentheses. The subpatterns are matched against the values for the keys:
>>> def sides := ["square" => 4, "triangle" => 3]
... def shape := "triangle"
...
... def ["square" => squareSides, (shape) => qty1] := sides
...
... def ["triangle" => qty2] | _ := sides
...
... [squareSides, shape, qty1, qty2]
[4, "triangle", 3, 3]
‘:=’ may be used to specify a default value to match a subpattern against if the key is absent:
>>> def sides := ["square" => 4, "triangle" => 3]
...
... def ["octogon" => octoSides := 8] | _ := sides
... octoSides
8
The importer syntax without keys is a shortcut for binding names identical to string keys in a map:
>>> def sides := ["square" => 4, "triangle" => 3]
...
... def [=> triangle, => square] := sides
... [triangle, square]
[3, 4]
List Pattern Expansion¶
>>> m`def [item1, item2] + rest := stuff`.expand()
m`def via (_splitList.run(2)) [item1, item2, rest] := stuff`
Map Pattern Expansion¶
>>> m`def ["key" => patt] := data`.expand()
m`def via (_mapExtract.run("key")) [patt, _ :_mapEmpty] := data`
>>> m`def ["key1" => patt1] | rest := data`.expand()
m`def via (_mapExtract.run("key1")) [patt1, rest] := data`
>>> m`def ["key1" => patt1 := fallback] := data`.expand()
m`def via (_mapExtract.withDefault("key1", fallback)) [patt1, _ :_mapEmpty] := data`
The Same and Not Same Patterns¶
Non-associative.
SamePatt
NotSamePatt
Same patterns match objects that compare same to their value.
>>> def state := "night"
...
... switch (state) {
... match =="day" {"night"}
... match =="night" {"day"}
... }
"day"
Not-same patterns match objects that do not compare same to their value:
.. todo:: test "bigMoney" =~ !="bankrupt"
Exact Pattern Expansion¶
>>> m`def ==specimen := value`.expand()
m`def via (_matchSame.run(specimen)) _ := value`
>>> m`def !=specimen := value`.expand()
m`def via (_matchSame.different(specimen)) _ := value`
The Quasi-Literal Pattern¶
Non-associative.
QuasiliteralPatt
Quasiliteral patterns invoke a quasiparser with text containing pattern holes. The resulting matcher object is invoked with the object to be matched, and the patterns in the holes are matched against the specimens it extracts:
>>> "The cat and the hat." =~ `The cat and the @what.`
true
>>> "The cat and the hat." =~ `The cat and the @{what :Str}.`; what
"hat"
>>> "The cat and the hat." =~ `The cat and the @{what :Int}.`
false
Quasi-Literal Pattern Expansion¶
>>> m`def ``quasi @@patt`` := value`.expand()
m`def via (_quasiMatcher.run(::"````".matchMaker(_makeList.run("quasi ", ::"````".patternHole(0), "")), _makeList.run())) [patt] := value`
The via Pattern¶
ViaPatt
Via patterns contain a view (sometimes called a transformation) and a subpattern. The view is an expression which takes a specimen and ejector and returns a transformed specimen on success or ejects on failure. This is similar to a guard but permits much richer transformations in addition to simple tests:
>>> def via (_splitList.run(1)) [x, xs] := [1, 2, 3]
... [x, xs]
[1, [2, 3]]
Final Pattern (kernel)¶
FinalPatt
Final patterns match an object and bind a name to them, optionally testing them for guard conformance. One of the most ubiquitous patterns. Binds a name unconditionally to a final slot, which prohibits reassignment:
>>> def x := 1
... x
1
Again, any string can be used as an identifier:
>>> def ::"hello, world" := [1, 2]
... ::"hello, world"
[1, 2]
The var Pattern (kernel)¶
VarPatt
Var patterns match an object and bind a mutable name to them, optionally testing them for guard conformance. Guard conformance failure causes pattern match failure. Later assignments to ‘x’ will be tested for guard conformance as well.
var name := value
var name :Guard := value
Like a final pattern, but with VarSlot
as the slot, which permits
reassignment to the name later on using an assign expression.
Note
While var
can be used to introduce a var pattern, the overall
expression is still a def expression, and it can alternatively be
expressed as:
def var name := value
This is useful for nesting var patterns within other patterns:
def [first, var second] := value
Bind Pattern¶
BindPatt
Bind patterns match an object and bind it to a forward-declared name, optionally testing for guard conformance.
bind x
bind x ::"hello, world"
bind x :G
Expansion¶
>>> m`def bind x := 2`.expand()
m`def via (_bind.run(x_Resolver, null)) _ := 2`
Slot Pattern¶
SlotPatt
Slot patterns match an object and bind them to the slot of the pattern’s name, optionally testing the object for guard conformance.
def &name := slot
Slot Pattern Expansion¶
>>> m`def &x := 1`.expand()
m`def via (_slotToBinding) &&x := 1`
Binding Pattern (kernel)¶
BindingPatt
Binding patterns match an object and use it as the binding for the given name.
&&x
&&::"hello, world"
A bind pattern does not bind a name, but binds a binding.
def &&name := binding