nevalang / neva

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

Deferred Connections 2.0 (Simplified with Trigger semantics) #717

Open emil14 opened 13 hours ago

emil14 commented 13 hours ago

Problem

Deferred connections were invented to solve the problem that caused by constant implementation - New component is infinite emitter. As a result everywhere where constants are used we need to somehow limit them. To avoid explicit Lock bloat we added -> (...) syntax. This however has some problems:

  1. It makes (manual) analysis of a program (tracing) harder because of lots virtual of lock:sig and lock:data entities moving data around
  2. This technique really defers receiving, not sending, that's one of the reasons we can't have buffered channels by default, see #681 for details
  3. Another form of connection exist, that also makes programs a bit more noisy. (As a counterpart - deferred connections might still remain because they are solving their own problem, that is mostly critical for constants, but not exclusively existing with them)

Solution

At least for constants consider usage of chained connections instead of deferred. Instead of x -> (y -> z) write x -> y -> z where y is constant reference or message literal.

Before

:foo -> (42 -> :bar)

Translates to:

const s = 'definitely not a valid URL'

// ...

#bind(s)
send42 New
---
:foo -> lock:sig
send42 -> lock:data
lock:data -> :bar

Trace:

sent | :foo | {}
send | send42 | 42
recv | lock:sig | {}
recv | lock:data | 42
sent | send42 | 42 // at least 1 more time in this case
sent | lock:data | 42
recv | :bar | 42

After

:foo -> 42 -> :bar

Translates to:

const s = 'definitely not a valid URL'

// ...

#bind(s)
send42 NewV2
---
:foo -> newV2:sig
newV2:data -> :bar

Trace:

sent | :foo | {}
recv | send42 | {}
sent | send42 | 42 // exactly 1 time in this case
recv | :bar | 42

Benefits

  1. No () noise (especially good for nested defers)
  2. No sent | __newXXX__ and __lockXXX__ noise in trace logs
  3. Less channels and function calls, less message passing (better performance and memory consumption)
  4. Ability to add automatic buffers

No () noise

Maybe especially good when defer and chain are combined:

:start -> [(1 -> add:acc), (2 -> add:el)]

// vs

:start -> [1 -> add:acc, 2 -> add:el]

Please note that #617 must be implemented for this

Steps To Implement

  1. Implement NewV2 (with obviously better name)
  2. Make changes to desugarer to support chained connections with const refs / msg literals
  3. Consider removing deferred connections (must be thought of very carefully, this is still powerful feature - they solve real problem and they are universal in a way that they can infinitely nest!)
emil14 commented 13 hours ago

BTW New might not be the best name for what it is