nevalang / neva

🌊 Dataflow programming language with static types and implicit parallelism. Compiles to machine code and Go
https://nevalang.org
MIT License
128 stars 8 forks source link

`Cond` syntax sugar #729

Open emil14 opened 1 month ago

emil14 commented 1 month ago

Idea

cond(if, data) {
    then -> ...
    else -> ...
}

Before

42 -> cond:data
true -> cond:if
cond:then -> :yes
cond:else -> :no

After

cond(true, 42) {
    then -> :yes
    else -> :no
}

Why Cond is irreplaceable by switch

Cond cannot be replaced with switch because in switch both data and [case] are the same thing, while cond:if and cond:data are not. Data is anything and :if is always bool. I.e. it's not possible with switch to route data based on some condition other than value of that data.

Why Cond is irreplaceable by deferred connections?

Short answer: It solves race-conditions (this is why it was introduced in the first place).

Long answer:

Let's try to recreate this example with switch and defers

true -> switch {
    true -> { 42 -> :yes }
    false -> { 42 -> :no }
}

A little bit more repetitive than cond statement but still ok and without need for extra syntax. However, the way it desugars, creates unintuitive race-condition:

condition -> switch:data
42 -> lock1:data
42 -> lock2:data
switch:case[0] -> lock1:sig
switch:case[1] -> lock2:sig
lock1:data -> :yes
lock2:data -> :no

Note that we replaced true with condition - it means condition is dynamic now. Now let's consider this set of events:

  1. condition=true -> switch:data
  2. condition=false -> switch:data

We expect sending to :yes first and to :no second right? But depending on undefined conditions (such as CPU temperature or phase of the moon) we might get different results! Let's continue:

  1. switch:case[0] -> lock1:sig
  2. switch:case[0] -> lock1:sig

Still everything ok but what we might get next?

  1. lock2:data -> :no
  2. lock1:data -> :yes

Race condition! Out of order delivery! But why? Well, because lock1 and lock2 are two separate goroutines that operate concurrently. Not just lock2, despite receiving second, might finish its job and send faster than lock1, even more than that - both lock1 and lock2 might be suspended and for some reason Go scheduler might run lock2 before lock1.

This is the reason Cond (and some other components) were introduced. Order of delivery can be controller at the level of one runtime function, but not multiple concurrent onces.

On missing language feature

It feels like this problem should be solved by some language feature like deferred connections but different one. I have no idea what it is at the moment though.

On need for this syntax

I should probably note existing way of using cond - without special syntax for it - is far form perfect but managable. So it's questionable if we truly need this.