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

Match statement (syntax sugar) #726

Closed
emil14 opened this issue Oct 4, 2024 · 4 comments
Closed

Match statement (syntax sugar) #726

emil14 opened this issue Oct 4, 2024 · 4 comments
Assignees

Comments

@emil14
Copy link
Collaborator

emil14 commented Oct 4, 2024

See #724 and #725 for context

Before (desugared)

def Handler(data int) (sig any) {
	Match, Println
	---
	:data -> match:data
	1 -> match:if[0]
	'one' -> match:then[0]
	2 -> match:if[1]
	'two' -> match:then[1]
	3 -> match:if[2]
	'three' -> match:then[2]
	'four' -> match:else
	match -> println -> :sig
}

After (sugar)

???

Note about Rust

In Rust match is very universal thing - it can replace both if-else and switch. It's an expression that always returns some value, but actually you don't have to return anything from your branches, it will return unit in that case (think void), so it's possible to use it for both "selecting" and "routing" (triggering control-flow branch).

Also checkout https://doc.rust-lang.org/book/ch18-00-patterns.html

Implementation Info

If match is only selector, then it's basically a map between senders!

type Match struct {
    Data Sender
    Cases map[Sender]Sender
    Else Sender
}

If it's router (but Switch is more suitable for that and we still need selector), then it would be basically map of senders to receivers

type Match struct {
    Data Sender
    Cases map[Sender]ReceiverSide
    Else Sender
}

(note that sender is one, but receivers are slice (only router, for selector it's 1-1))

On outgoing ->

Based on previous point of view we do need outgoing -> for selector, and we do not need it for router.

Note on Rust...

Since in Rust it's both, it's possible to imagine that in some cases we use match as selector and in some as router (kinda similar to assigning returned value from match to a variable in Rust)

Selector

... -> match {
  senderA: senderX
  senderB: senderY
} -> ...

### Router

... -> match {
  senderA -> receiverSide1 // using ->
  senderB -> receiverSide2 // because they're (almost?) connections
}

I've used incoming -> in the example above, but it doesn't have to be like that.

On incoming ->

Regardless being selector or router (which affect outgoing ->), incoming arrow might be or might not be present, depending on whether want match to be part of the chained connection (only) or not

Incoming -> is present

For struct selectors motivation was to allow having different selectors on different multiple receivers:

v -> [
    .x -> foo
    .y -> bar
]

Which wasn't possible with selector as a modified for sender

v.x -> foo
v.y -> bar

v is used twice here, and sender must not be reused. It maybe possible to teach desugarer to handle this with v -> fanOut -> [.x, .y], but that's much more complicated than existing implementation.


In this case match is sender, that (just like struct selector) is only allowed as a chain-head.

v -> match {...} -> ...

Which means this syntax only support match as a selector, but not as a router. Because sender must have receiver side and router doesn't have single receiver side to have after outgoing ->.

It also means for Switch syntax, that it cannot be implemented like this. Switch has to be receiver-side thing.


It also means that match as both selector and router at the same time is hard to implement because it has to be supported at both sender and receiver sides.


Match vs Ternary and Switch vs If-Else

Match can replace ternary (as it's more powerful form) and switch can replace if-else (in the same way). But ternary is probably more ergonomic that match. It's questionable if we can say that about switch with if-else.

@emil14
Copy link
Collaborator Author

emil14 commented Nov 3, 2024

I was thinking about #639 but the problem is you still have to have all the incoming connections noise (and it doesn't make match specifically universal, like in Rust)

42 -> match:data

1 -> match:case[0]
2 -> match:case[1]
3 -> match:case[2]

match -> {
    :case[0] -> branch1
    :case[1] -> branch2
    :else -> branch3
}

@emil14
Copy link
Collaborator Author

emil14 commented Nov 3, 2024

Given time limitations and what we understand so far I think we need to make a decision:

  1. match and switch are two separate things, one for selecting a value, another for routing the message to correct branch.
  2. match being selector must be type of sender, because it needs to have receiver-side to send selected message downstream
  3. incoming arrow -> is presented and match sender is only allowed inside chained connections - for base cases it doesn't make a difference (and users already introduced to chained connections anyway), but for more complex cases where you (for some reason) need to use match inside chain, it will allow this

as a side-point having incoming -> might be more idiomatic for Nevalang, even though we already brake this rule for binary and ternary expressions, but it's not possible to make them otherwise

Example:

data -> match {
    1: one
    $two: three:four
    senderThree: $five
    else: 'six'
} -> println

@emil14 emil14 changed the title Match syntax sugar Match statement (syntax sugar) Nov 3, 2024
@emil14
Copy link
Collaborator Author

emil14 commented Nov 3, 2024

Pattern Matching

How all this plays with #747?

@emil14
Copy link
Collaborator Author

emil14 commented Dec 15, 2024

close in favor of #805

@emil14 emil14 closed this as completed Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant