Seems like Rust is still working on this area with the controversial keyword generics
In C#, they implement Await/Async by converting the function into a Class, just like when you use 'yield return'. Control flow is all over the place.
Zig is so strict about "no hidden control flow" that you can't even have destructors (code which runs when a variable goes out of scope)
Zig just needs some runtime event loop like Tokio or AsyncIO from Rust to get up and running with its fantastic async model.
Cool, I like this!
I realized last year that I was investing too much time into sharing things on Twitter that I'd subsequently forget about or be unable to find.
Instead, I created a separate section on my personal blog for "notes" where the idea is to house content that I'd otherwise post to Twitter. It's been working well, and I like owning my own content rather than contributing it to another platform.
I'm especially glad for this change after seeing what's happened in the past few months with Twitter and Reddit. It's been unfortunate to see those platforms become so much more possessive of the content that their users generated. If you publish to your own platform, you're safe from that. At least until LLMs bury your platform in noise.
To make it as convenient as possible (every amount of friction would eat at my motivation) I made it using Obsidian Digital Garden [1]. It's a bit of a hassle to set up and the UI is annoyingly bad but it's pretty convenient (boils down to click to publish) afterwards.
Curious, but how exactly would they do that? Are you talking about scraping your website and adding garbage filler content from LLMs and republishing to divert traffic?
When it comes to e.g. memory management, Zig tries to be unopinionated and allow different implementations to be implemented as desired; so it seems odd to bake-in something like async/await (even if the execution strategy of those computations is up to the user).
I've seen this happen in many high-level languages (JS, Python, PHP, etc.), which I mostly attribute to (a) ignorance of those generalisations, and (b) a band-wagon effect. The unfortunate result in those languages is a bloated mess of try/catch, async/await, for/yield, apply/return, etc. and all of their O(n!) possible interactions; which could have instead been implemented as libraries on top of a single primitive (e.g. shift/reset, or whatever)
[0]: AFAIK these are all equivalently expressive, and given one it's easy enough to write the others as libraries.
PS: I recall asking this question when PHP added generators; I can't seem to find a bug report or mailing list post though...
- 'Yield: Mainstream Delimited Continuations' builds them up by generalising for/yield (rather than try/throw), but the result is the same: https://www.researchgate.net/publication/228584945_Yield_Mai...
- 'A Poor Mans Concurrency Monad' seems to be the origin of async/await pattern (according to https://softwareengineering.stackexchange.com/a/377514/11211... ), which is defined as a monad (technically a monad transformer) https://www.cambridge.org/core/journals/journal-of-functiona...
Incidentally, monad transformers are an attempt to work-around a deficiency of monads: that they don't compose. Algebraic effects have become popular precisely because they do compose.
The key requirement for all these is an ability/API to defer and resume execution (indeed, delimited continuations are sometimes described as "resumable exceptions"). In higher-level languages we'd just assume the presence of first-class functions/closures, and use those to describe these features. I'm less familiar with how that looks in a very low-level language like Zig, however these "async frames" appear (to my naïve eyes) to be analagous; hence why I'm interested whether one of those more-general primitives could be provided instead (the answer might be no!).
As for clarification on those features, here's a quick attempt. Firstly, note that all of these approaches are basically APIs to construct, consume, and combine (deferred) computations: they are unopinionated on how those computations get run (e.g. the user could supply a "main loop", or whatever).
Delimited continuations are like exceptions, except the stack (AKA continuation) is passed to the handler, which may choose to resume it. We can implement coroutines/async/yield/etc. by having handlers which put their continuation in a queue and pop off some other one to resume instead; we can get data parallelism by resuming a continuation many times; we can get backtracking by remembering old continuations and trying them again; we can get parsers, probabilistic programming, nondeterminism, etc. https://en.wikipedia.org/wiki/Delimited_continuation
Algebraic effects are similar, but defunctionalised: i.e. they represent control flow with datatypes, and are "interpreted" by a user-specified function.
Applicative is an API for combining two computations concurrently, e.g. the product of Foo and Bar is a single computation yielding a pair of results. Monad can likewise combine sequentially, where Bar depends on the result of Foo. These feel more abstract, but are equivalent to the above e.g. see https://okmij.org/ftp/continuations/implementations.html https://reasonablypolymorphic.com/#understanding-freer-monad...
[0] https://docs.godotengine.org/en/stable/tutorials/scripting/g...
"A delayed game is eventually good, but a rushed game is forever bad." - Shigeru Miyamoto
The same goes for features. I'd rather Zig have a delayed/good async next year and forever after, instead of a rushed/bad async right this moment and forever after.
Andrew Kelley is the greatest, most productive Yak shaver of all time.