emdash / udlang

A practical, functional language for stream processing.
GNU Lesser General Public License v3.0
1 stars 0 forks source link

Coroutines #21

Closed emdash closed 2 years ago

emdash commented 3 years ago

Problem Statement

If you squint hard enough, uDLang's out statement looks suspiciously like a "yield". This proposal embraces that similarity.

At minimum, this is a case for changing the keyword from out to yield, with an eye toward one day generalizing the IO mechanism to also handle co-routines.

There is currently a yield keyword, but it is used to return values from a block. This could be changed to return, or simply omitted as in Rust, or we could choose a different keyword or operator.

Modeling side effects in the type system.

Side effects are not modeled by the type system in the following sense: every expression conceptually has an associated set of outputs. There is one global output type which applies to all of them. It is not possible to define a local output type.

Concretely, if an expression in uDLang nominally has type T as in func foo() -> T ...; foo();, then it can be conceptually thought of as really having type (T, [Output]), where Output is the output type declared by the script. And simple statement foo(bar(3)); really represents:

let b3 = bar(3);
(void, b3[1] + foo(b3[0])[1])

Where + represents concatenation of the two output sequences.

For reasons having to do with both performance, and personal preference, uDLang avoids explicitly representing side-effects this way. The goal of uDLang is to be an accessible functional language. I want users of uDLang to be able to pick it up and run with it, without first having to understand esoteric topics in CS theory, like monoids, monads, functors, endofunctors, and categories. Not least of which, because I don't fully understand them yet.

Towards co-routines

Suppose we extend the language to allow capturing and operating on the side effects directly. Here's some proposed syntax. ouptut ( ... ), which evaluates to an iterable containing the output of an expression or block.

Suppose we allow locally re-declaring the output type of a function. For example func foo() ->T yielding Int, declares that out statements within foo expect an integer, overriding whatever type is expected by the outer scope.

This begins to look suspiciously like language support for co-routines.

To be clear, the out mechanism already shares some DNA with the yield construct provided by languages like python and JavaScript. But yield is both an output and an input. In many implementations of co-routines, values can be injected into the co-routine at yield points.

In general, I like co-routines. They are an elegant, powerful way to express many concepts. But I am not convinced they belong in uDLang -- at least not right now. In imperative languages, they serve as a useful bridge between the stateful and the stateless, allowing a big nasty pile of internal mutation to be viewed externally as a nice, clean (possibly infinite) sequence of immutable values.

My goal with uDLang has been to avoid any kind of explicit mutable state. There might be some state hidden in the implementation, but this is only exposed to the uDLang code in a controlled way. Adding co-routines now starts to feel like opening the door to eventually allowing for mutable local variables, which, when hiding inside a co-routine bound to a global value would leads to expressions whose values can change each time you look at them, which begins to look suspiciously like (surprise!) imperative code!

On the other hand, it might be the cleanest and simplest way to support things like RNGs and counters, which would be undeniably useful. Before we can answer the question of whether or not co-routines belong in the language, we have to answer the question of just how important it is that uDLang be a pure functional language. How important is it to me that uDLang be truly stateless.

emdash commented 2 years ago

I no longer plan to support this. The first release of uDLang will be a pure functional language with no interleving of side-effects.