<p>I find it interesting to have stackless asynchronous I/O, e.g. when using an HTTP-client in the code, the call-stack get emptied before handling next &quot;event&quot; (could be unrelated or the response), and some time later the response arrives as an &quot;event&quot;.</p>
<p>As how I understand it, this is how Node.js is working, the application code is a single thread that consume and handles events.</p>
<p>JavaScript on Node.js has been implemented using:</p>
<ul>
<li>closure call back functions</li>
<li>promises</li>
<li>async-await as a syntactic sugar on-top of promises.</li>
</ul>
<p>Are there any other ways programming languages has (or could) implemented this kind of single-threaded stackless asynchronous I/O? How so?</p>

<async><call-stack><io><concurrency>
Alternatives for stackless async I/O implementation?
CC BY-SA 4.0
2024-09-12T23:01:53.737Z
It's not super clear to me how to answer this question. For example, async can be implemented in a straightforward manner in a language with coroutines, or a language with effect handlers , but surely then the question simply becomes "what are techniques for implementing coroutines / effect handlers / whatever". I'm not quite sure what you're looking for; can you say a bit more about what is motivating the question?
Eric Lippert
CC BY-SA 4.0
2024-09-12T23:07:25.250Z
@EricLippert I am trying to explore alternatives to call-back functions and async-await (both used by Node.js), but still have the stack-less model where the call-stack gets emptied, (e.g. most coroutine implementations I have learned about e.g. Lua uses "threads" with their own call-stacks.) I would like to keep it single-threaded as in Node.js, but be able to process concurrent requests by using an event-queue.
Jonas
CC BY-SA 4.0
2024-09-12T23:46:54.363Z
the answer to this is just CPS, no? full CPS is inherently stackless and can be used to implement async fine
blueberry
CC BY-SA 4.0
2024-09-12T23:52:43.180Z
@blueberry what do you mean with "CPS"?
Jonas
CC BY-SA 4.0
2024-09-13T00:24:42.613Z
Continuation Passing Style. The generalization of your "callback functions and closures". In a CPS program there are no returns at all; "returning" is just calling the provided continuation with the "returned" value. Since no call returns, there is no need to have a call stack, because the purpose of the stack is to remember where you're returning to, and in CPS, you're not returning.
Eric Lippert
CC BY-SA 4.0
2024-09-13T00:29:07.337Z
If you want a quick primer on CPS, I wrote a couple of blog posts a few years ago on how to take a simple recursive algorithm in Python and remove all the recursions from it. https://ericlippert.com/2018/12/
Eric Lippert
<h3>Truly stackless</h3>
<p>Let's start with call stack frames. These are a very convenient invention that enables automatic temporary storage in a very compact form. This was paramount on early machines where every byte counts. It's also very convenient as they create a happens-before type of relationship, because all &quot;effects&quot; of a function can be thought as already committed at the return of call.</p>
<p>After, let's consider the implementation of <code>async</code> in C#. Instead of changing the compiler and the interpreter/VM, it was only necessary to change the compiler, as to transform any <code>async</code> method into a normal object. All the machine state stuff and multiple start and reentry logic can (and it was) abstracted and transformed in a plain old object, that double as an closure (capture state) and machine state (separated code segments for each source code segmented between <code>await</code> keywords).</p>
<p>So, call <em>stack frames</em> can be transformed into call <em>state objects</em>. But there is the insight: with all your call state being first class, or at least normal object data, your language is now <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" rel="nofollow noreferrer">colorless</a>.</p>
<p>In general, call stacks are &quot;native&quot;, &quot;binary&quot;, all at all very different from the rest of an interpreted or managed language. This &quot;call stack is different&quot; that causes these discussions about call stack moving in and out on events or effects. But no juggling around is necessary if in each call state object there is space for each temporary that this specific function call instance may use.</p>
<p>A language like this, beyond being stackless and colorless, is also very easy to transpile in other languages, because all the source code can now be represented as a compiled version of source code where <em>all of everything</em> is a heap allocated object, with very clear imperative semantics.</p>
<p>Note this is no different from saying that async/await, coroutines, effects and CPS can be simulated in imperative languages, but that is not my point.</p>
<p>What I'm saying is that by eliminating the call stack completely, discussions about threads, callbacks and event queues almost disappear, as they are specific, <em>imperative</em> implementation details <em>workarounds</em>, that are used to express these other forms of eventual, concurrent, non-imperative execution.</p>

CC BY-SA 4.0
2024-09-13T13:48:48.293Z
2024-09-13T19:14:42.517Z
2024-09-13T19:14:42.517Z
André L F S Bacci
# Alternatives for stackless async I/O implementation?
Posted by: username_0 on 2024-09-12
I find it interesting to have stackless asynchronous I/O, e.g. when using an HTTP-client in the code, the call-stack get emptied before handling next "event" (could be unrelated or the response), and some time later the response arrives as an "event".

As how I understand it, this is how Node.js is working, the application code is a single thread that consume and handles events.

JavaScript on Node.js has been implemented using:

* closure call back functions
* promises
* async-await as a syntactic sugar on-top of promises.

Are there any other ways programming languages has (or could) implemented this kind of single-threaded stackless asynchronous I/O? How so?
**Tags:** async call-stack io concurrency
> **Comments:**
> **username_1**: It's not super clear to me how to answer this question. For example, async can be implemented in a straightforward manner in a language with coroutines, or a language with effect handlers , but surely then the question simply becomes "what are techniques for implementing coroutines / effect handlers / whatever". I'm not quite sure what you're looking for; can you say a bit more about what is motivating the question?  
> **username_0**: @EricLippert I am trying to explore alternatives to call-back functions and async-await (both used by Node.js), but still have the stack-less model where the call-stack gets emptied, (e.g. most coroutine implementations I have learned about e.g. Lua uses "threads" with their own call-stacks.) I would like to keep it single-threaded as in Node.js, but be able to process concurrent requests by using an event-queue.  
> **username_2**: the answer to this is just CPS, no? full CPS is inherently stackless and can be used to implement async fine  
> **username_0**: @blueberry what do you mean with "CPS"?  
> **username_1**: Continuation Passing Style. The generalization of your "callback functions and closures". In a CPS program there are no returns at all; "returning" is just calling the provided continuation with the "returned" value. Since no call returns, there is no need to have a call stack, because the purpose of the stack is to remember where you're returning to, and in CPS, you're not returning.  
> **username_1**: If you want a quick primer on CPS, I wrote a couple of blog posts a few years ago on how to take a simple recursive algorithm in Python and remove all the recursions from it. https://ericlippert.com/2018/12/  
---
## 1 answers
### Answer by username_3 on 2024-09-13. Score: 1
### Truly stackless

Let's start with call stack frames. These are a very convenient invention that enables automatic temporary storage in a very compact form. This was paramount on early machines where every byte counts. It's also very convenient as they create a happens-before type of relationship, because all "effects" of a function can be thought as already committed at the return of call.

After, let's consider the implementation of `async` in C#. Instead of changing the compiler and the interpreter/VM, it was only necessary to change the compiler, as to transform any `async` method into a normal object. All the machine state stuff and multiple start and reentry logic can (and it was) abstracted and transformed in a plain old object, that double as an closure (capture state) and machine state (separated code segments for each source code segmented between `await` keywords).

So, call *stack frames* can be transformed into call *state objects*. But there is the insight: with all your call state being first class, or at least normal object data, your language is now [colorless](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/).

In general, call stacks are "native", "binary", all at all very different from the rest of an interpreted or managed language. This "call stack is different" that causes these discussions about call stack moving in and out on events or effects. But no juggling around is necessary if in each call state object there is space for each temporary that this specific function call instance may use.

A language like this, beyond being stackless and colorless, is also very easy to transpile in other languages, because all the source code can now be represented as a compiled version of source code where *all of everything* is a heap allocated object, with very clear imperative semantics.

Note this is no different from saying that async/await, coroutines, effects and CPS can be simulated in imperative languages, but that is not my point.

What I'm saying is that by eliminating the call stack completely, discussions about threads, callbacks and event queues almost disappear, as they are specific, *imperative* implementation details *workarounds*, that are used to express these other forms of eventual, concurrent, non-imperative execution.
---
