Right now pattern matching is not very clearly defined, so this issue attempts to resolve that.
Matching against existing variables, vs introducing new variables in patterns
One problem I have with Rust's pattern matching, is that there's no way of matching against an existing variable. Each identifier introduces a new variable, eg. in Some(x), x is a new variable, in Thing { x: y }, y is also a new variable. Thus in tsuki I want to have a proper syntax for introducing variables into scope, vs matching against existing values.
Since bringing values into scope is a more common use case than matching existing variables, the syntax val x can be used to match against an existing variable.
let outer = 1
match thing
Some(val outer) -> print("it's the outer value!")
Some(inner) -> print(inner)
Nil -> print("nothin' to see here")
The patterns
We should support a fairly limited, yet flexible set of patterns in the beginning.
The wildcard pattern _
Matches anything and discards it.
Literal patterns, such as 1, true, :my_atom, "Hello there."
Matches a value literally, using the equality operator ==.
Range patterns, such as 1..5, 1..<5.
Matches a value between a specified range.
Outer variable patterns, such as val abc
Matches a value and compares it to the one stored in the variable abc, using the equality operator ==.
Variable binding patterns, such as abc, var abc
Matches a value and introduces it into scope under a user-specified name and optional mutability.
Moves the matched value into the new variable in the process.
Tuple pattern, such as (x, var y).
Matches each tuple field.
Union variant patterns, such as Some(x), MyUnion.MyVariant(var a, var b)
Matches each of the variant's fields.
Object patterns, such as MyObject { some_field = x, another_field = var y }
Matches individual object fields.
Refutability
A pattern is irrefutable if it can be proved to always match, no matter the input. A pattern is refutable if it can be proved to not match sometimes, given a specific set of inputs. These are mutually exclusive, ie. a pattern that is not irrefutable is refutable, so only specifying one of these rules is enough.
A pattern is refutable if it contains any of the following patterns:
Literal patterns,
Variable patterns,
Union variant patterns, but only if the matched value's type is not the union variant type itself.
Any pattern that does not contain any of the aforementioned patterns is irrefutable.
Unifying assignment
With patterns introduced, assignment should be unified such that it uses pattern matching instead of some arbitrary keywords. Having assignment return the old value is quite a nice feature to have, so the existing = operator is not going anywhere. A new let statement could be introduced for matching. It would obsolete the existing val and var statements in favor of taking a pattern to match against on its left hand side. This code:
val x = 1
var y = 2
val _ = x
would instead be written as:
let x = 1
let var y = 2
let _ = x
Although it looks a little verbose at first, it is a lot more flexible, as it allows for matching against patterns:
let this_is_surely_some = Some(1)
let Some(one) = this_is_surely_some
let (x, y) = (1, 2)
let also becomes part of the if statement and while loop.
if let Some(x) = maybe_nil
print(x)
while let Some(x) = my_iterator.next
print(x)
As before, it should be possible to specify multiple patterns, all of which must match.
if let Some(x) = maybe_nil, let Nil = surely_nil
()
for loops with patterns
for loops should also use this pattern syntax, always expecting an irrefutable pattern before the in keyword.
for x in [1, 2, 3]
print(x)
As demonstrated before, more complex matches can be made.
for (x, y) in [(1, 2), (2, 3), (3, 4)]
print((x, y))
Right now pattern matching is not very clearly defined, so this issue attempts to resolve that.
Matching against existing variables, vs introducing new variables in patterns
One problem I have with Rust's pattern matching, is that there's no way of matching against an existing variable. Each identifier introduces a new variable, eg. in
Some(x)
,x
is a new variable, inThing { x: y }
,y
is also a new variable. Thus in tsuki I want to have a proper syntax for introducing variables into scope, vs matching against existing values.Since bringing values into scope is a more common use case than matching existing variables, the syntax
val x
can be used to match against an existing variable.The patterns
We should support a fairly limited, yet flexible set of patterns in the beginning.
_
1
,true
,:my_atom
,"Hello there."
==
.1..5
,1..<5
.val abc
abc
, using the equality operator==
.abc
,var abc
(x, var y)
.Some(x)
,MyUnion.MyVariant(var a, var b)
MyObject { some_field = x, another_field = var y }
Refutability
A pattern is irrefutable if it can be proved to always match, no matter the input. A pattern is refutable if it can be proved to not match sometimes, given a specific set of inputs. These are mutually exclusive, ie. a pattern that is not irrefutable is refutable, so only specifying one of these rules is enough.
A pattern is refutable if it contains any of the following patterns:
Any pattern that does not contain any of the aforementioned patterns is irrefutable.
Unifying assignment
With patterns introduced, assignment should be unified such that it uses pattern matching instead of some arbitrary keywords. Having assignment return the old value is quite a nice feature to have, so the existing
=
operator is not going anywhere. A newlet
statement could be introduced for matching. It would obsolete the existingval
andvar
statements in favor of taking a pattern to match against on its left hand side. This code:would instead be written as:
Although it looks a little verbose at first, it is a lot more flexible, as it allows for matching against patterns:
let
also becomes part of theif
statement andwhile
loop.As before, it should be possible to specify multiple patterns, all of which must match.
for
loops with patternsfor
loops should also use this pattern syntax, always expecting an irrefutable pattern before thein
keyword.As demonstrated before, more complex matches can be made.