Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Pattern matching #44

Open
WhiteBlackGoose opened this issue Jun 16, 2022 · 2 comments
Open

[WIP] Pattern matching #44

WhiteBlackGoose opened this issue Jun 16, 2022 · 2 comments
Labels
Language idea One single idea which may be a start of a design document Syntax This issue is about syntax

Comments

@WhiteBlackGoose
Copy link
Member

WhiteBlackGoose commented Jun 16, 2022

So let's discuss what existing languages have, and what we want.

Potential features

1. Pattern matching over constants

This one is the simplest one. For example:

match a
| 5 -> ...
| 10 -> ...

2. Pattern matching over inheritors

Checking if a given instance is a subtype of a type:

match animal
| Dog d -> d.Bark()
| Cat c -> c.Meow()

3. Deconstruction

We may allow our types to be deconstructed for pattern matching, e. g. if type is a record or just defines this method

type Expr
type Constant = { Value: Float }
type Div = { Left: Expr, Right: Expr }
type Sqr = { Arg: Expr }

Then we could do

match expr
| Div(Sqr(a), Sqr(b)) -> Sqr(Div(a, b))
| other -> other

4. Arbitrary condition clause

If we can't express something with pattern matching rules

match expr
| Sum(Sqr(Sin(x)), Sqr(Cos(y))) when x = y -> Constant 1
| other -> other

5. Branch merge

We might have different cases, but in all of them we need the same value:

match expr
| Sum(0, x) | Sum(x, 0) -> x
| other -> other

6. Active patterns

This is the least compile-time verified but the most functional thing, because customizeable

match email
| ValidEmail(name, domain) -> "Hello, ${name} from ${domain}!"
| other -> "bad email"

7. Operators on patterns

So that we could combine our conditions:

match animal
| IBark and IFeed and not IBites goodboi -> "Good boy"
| not IBark -> "Ok-ish"
| _ -> "evil"

8. Conditional deconstruction

We may have deconstruction method which returns boolean. It is less powerful than active patterns, but tied to a type, not a separate function:

type MyStateMachine<T> =
    Deconstruction(out T res) : boolean = if valid then res = _current; return true else return false

and

moveNext stateMachine =
    match stateMachine
    | MyStateMachine(res) -> "Valid and equals ${res}"
    | MyStateMachine -> "Invalid"
    | _ -> "other subtype"

9. Equality check

Matching against non-constant instances:

type Student = { Age: int, Name: string }
let john = Student(10, "aaa")
let hehe = Student(12, "bbb")

match student
| john -> "something"
| hehe or Student(30, "aaa") -> "else"

10. Mixed active patterns/deconstructions and constant matching

So, we partly deconstruct, and partly match

match animal
| cow(color: white, name) -> "this white cow's name is ${name}"
| _ -> "hmmm"

11. Specialised operators over primitives

E. g.

match a with
| > 5 and < 10 -> ...
| >= 6 -> ...
| _ -> ...

12. Property matching

E. g.

match frog with
| { SkinColor: green } -> "regular"
| _ -> "...."

What language implements what

Consider here three languages, as well as suggested features

Feature C# Kotlin F# Fresh proposed
1. Pattern matching over constants ✔️ ✔️ ✔️ ✔️
2. Pattern matching over inheritors ✔️ ✔️ ✔️
3. Deconstruction ✔️ ✔️
4. Arbitrary condition clause ✔️ ✔️ ✔️
5. Branch merge ✔️ ✔️
6. Active patterns ✔️ ✔️
7. Operators on patterns ✔️
8. Conditional deconstruction
9. Equality check ✔️
10. Mixed active patterns/deconstructions and constant matching ✔️ ✔️ ✔️
11. Specialised operators over primitives ✔️ ✔️ ✔️
12. Property matching ✔️ ✔️

Syntax overview

C# way

a switch
{
    ... => ...,
    ... => ...,
}

C# uses when for conditional clause and var to deconstruct. For the latter, var makes it clear that a variable is newly declared, but also is a bit wordy.

I don't like that C# needs , after each case. But I like that you're not forced to () around your expression.

Kotlin

when (a) {
    ... -> ...
    ... -> ...
    else -> ...
}

else is great. Other than that, not much to discuss since it lacks most major features of pattern matching.

F#

match a with
| ... -> ...
| ... -> ...

I like | instead of surrounding curly braces. But I don't like how wordy this match a with is.

What language has what

Again, these three languages + proposal for Fresh

Feature C# Kotlin F# Fresh proposed
Keyword switch when match ... with when
Position of keyword After Before Around After
() needed? No Yes No No
Surrounding braces Yes Yes No Depends*
Branch separator , (comma) \n (new line) | (pipe) Depends*
Arrow => -> -> ->
Default case _ else _ else

Depends*: so here I suggest single-line vs non-single:

val color = animal when Frog -> green | Dog -> black | Cat -> meow | else -> nothingElseMatters

val color = animal when {
    Frog -> green
    Dog -> black
    Cat -> meow
    else -> nothingElseMatters
}

Debates

Separator

If we don't want new line significant syntax, we will have to make cases not ambiguous. So we need a separator. Adding pipe makes it a bit wordy:

val color = animal when {
    | Frog -> green
    | Dog -> black
    | Cat -> meow
    | else -> nothingElseMatters
}

And, in my opinion, inconsistent with the rest of the syntax. It looks like we mixed ML and Kotlin into something weird.

Should when be the same as other control flow constructs?

if ... {
}
while ... {
}
... when {
}

may feel a bit inconsistent too.

@WhiteBlackGoose WhiteBlackGoose added Language idea One single idea which may be a start of a design document Syntax This issue is about syntax labels Jun 16, 2022
@LPeter1997
Copy link
Member

Just to have my opinion documented here too, I have 3 things: about the proposed syntax

  1. The when keyword is an odd choice to me. Most languages stich with match, switch or even case.
  2. The placement of the keyword of the proposed syntax is odd to me. As mentioned in the debates section, it feels out of place with the rest of the constructs.
  3. As I've understood, else is the "match anything but discard it" pattern. I feel that's a bit verbose and confusing when reading it, but maybe that's just me.

About features we want to support with it (numbered as you numbered them):

  1. Absolutely, let's kill the old switch
  2. Makes sense, this is how we can likely model our DUs down the line (and how I'm currently doing them from C#)
  3. Yes!
  4. Agree, but be careful about the keyword, when-when feels odd
  5. Absolutely, and the same names should be bindable to the same type in the different alternatives as long as it's the same type. I've missed this in C#.
  6. After learning about them, we might be better off with arbitrarily customizable patterns, like in Scala or Swift. Active patterns are a bit odd to me, and I believe with a nice pattern-matching "protocol", the feature (and more!) can be expressed way more nicely
  7. They feel weird, even in C#. I believe 4 should remove the need anyway?
  8. Almost the same as 6, with a nicely defined protocol this can become a way more powerful feature.
  9. I believe this would kill a lot of possibilities for optimization, but maybe not the worst thing ever. For now we should keep it not proposed I believe.
  10. Sure, that's just convenience I believe.
  11. Yes, but as mentioned in 6, we can really extend these for the end-user if we want to.
  12. Yes, convenience.

What I'm missing:

  • Should we consider sequence patterns?
  • What about string patterns (as a special case for sequence patterns)?

@nonamescm
Copy link

Just a suggestion, why don't you use ; for expressions separators? They could also be automatically inferred for any constructor that uses {}, Like:

animal when {
  Frog -> green;
  Cat -> {
     ...exprs...
  }
  Dogs(ds) -> for(dog in ds) {
    getColor(dog);
  }
}

The for and the block don't need ;.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Language idea One single idea which may be a start of a design document Syntax This issue is about syntax
Projects
None yet
Development

No branches or pull requests

3 participants