tsuki-lang / tsuki

An elegant, robust, and efficient programming language, that just lets you get things done.
MIT License
29 stars 2 forks source link

Pattern matching formalization #13

Open liquidev opened 2 years ago

liquidev commented 2 years ago

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.

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 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))
liquidev commented 2 years ago

Due to the syntax changes, a few other things will probably need to be changed: