Currently my best answer for this is the bind (>>=) operator (including, incidentally, one of its instances, `.then(...)`), but this is just fuzzy intuition if anything at all.
Edit: maybe it's actually implication? Since the previous line(s) logically imply the next. L_0 → L_1 → L_2 → L_n? Though this is non-commutative. Not sure, it's been a few years since my last metalogic class :P
Only for n = 0, I think. Otherwise, generalizing associative binary f_2 to f_n for all positive integers n is easily done inductively by f_1(x) = x and f_{n + 1}(x_1, ..., x_n, x_{n + 1}) = f_2(f_n(x_1, ..., x_n), x_{n + 1}), with no need to refer to an identity. (In fact, the definition makes sense even if f_2 isn't associative, but is probably less useful because of the arbitrary choice to "bracket to the left.")
A compiler could recognise that e.g. L_2 doesn't depend on L_1, and would be free to reorder them. And compilers do recognise this in terms of data dependence of operations.
You could treat the semicolon as an operator, and just like multiplication over matrices, it's only commutative for a subset of the general type.
Commutivity is a very light weight pattern, and so is correctly applicable to many things, and at any level of operation, as long as the context is clear.
[1] https://math.stackexchange.com/questions/785576/prove-the-co...
Or applied to the programming example, the statements:
1. Server.accept
2. Client.connect
3. File.write # write to completely unrelated file
123 = 312 ≠ 321.This is still a special case of what we mean by async wrt each other, because depending on the interleaving at each step and e.g. the data loaded into memory, the number of tasks may change, but the idea is that they still eventually terminate in a correct state.
I don't think it's sufficient to say that just because another term defines this concept means it's a better or worse word. "commutativity" feels, sounds, and reads like a mess imo. Asynchrony is way easier on the palette
Subtraction for instance is not commutative. But you could calculate the balance and the deduction as two separate queries and then apply the results in the appropriate order.
I can't agree. It is confusing, because you need to remember the blog post, it wouldn't be confusing in the slightest if you internalized the core idea. The question remains: is it worth it to internalize the idea? I don't know, but what I do know is some people will internalize it and try to do a lot of shit with this in mind, and after a while we will be able to see where this path leads to. At that point we will be able to decide if it is a good idea or not.
> "Asynchrony" is a very bad word for this and we already have a very well-defined mathematical one: commutativity.
It is risky to use "commutativity" for this. Zig has operators, and some of them are commutative. And it will be confusing. Like if I wrote `f() + g(). Addition is commutative, then Zig is free to choose to run f() and g() in parallel. The order of execution and commutativity are different things. Probably one could tie them into one thing with commutative/non-commutative operators, but I'm not sure it is a good idea, and I'm sure that this is the completely different issue to experimenting with asynchrony.
Still, you might then prefer a word as permutability, or swappability.
Except for loops which allow going backwards, and procedures which allow temporarily jumping to some other locally linear operation.
We have plenty of syntax for doing non-forwards things.
Fun fact: order does matter for addition. (When adding many floating-point numbers with widely varying exponents.)
The whole idea behind `await` is to make the old intuition work without the ugliness of `.then()`. `f(); await g(); h()` has exactly the expected execution ordering.
In JS, we designed `await` specifically to hide `.then()`, just as we had designed `.then()` because callbacks made tracking control flow (in particular errors) too complex.
I recall that one of our test suites was tens of thousands of lines of code using `then()`. The code was complicated enough that these lines were by and large considered write-only, partly because async loops were really annoying to write, partly because error-handling was non-trivial.
I rewrote that test suite using `Task.spawn` (our prototype for async/await). I don't have the exact numbers in mind, but this decreased the number of LoC by a factor of 2-3 and suddenly people could see the familiar uses of loops and `try`/`catch`.
a().then(() =>
b())
.then(() =>
c())
Compared to await a()
await b()
await c()
Even for this simple case I think it's much clearer. Then look at a more complex case: for( i=0; i<n; i++) {
await a(i) ;
}
Now try re-writing this with then() and see the difference.The latter is more fair, here is a possible solution:
const gen = (function* () {
for (let i = 0; i < n; i++) yield a(i);
})();
const run = next => !next.done && next.value.then(() => run(gen.next()));
run(gen.next());
Or something similar using reduce. But in both cases, it illustrates the point, I guess.But if we are at point we can introduce new keywords/syntax in the language, it would just as well possible to come with something like
a.chain(b, c)
In case you need to pass parameters a.chain([b, p1, p2], c)
And for the latter case const indexes = (function* () {
for (let i = 0; i < n; i++) yield i;
})
a.through(indexes)This isn’t always true at the language level, and almost certainly not at the CPU pipeline and microcode level.
Logic languages like Prolog will execute statements out of order, by design. Other languages like Mercury use the IO monad to signify serial operations
If instead you're referring to goals within the body of a clause, this is also incorrect. Goals are evaluated strictly left-to-right, and each must succeed before the next is attempted. This evaluation order is likewise required and observable, especially in the presence of side effects.
I was under the impression that when plugging holes during unification, that these statements/clauses could happen in any order just as you would like solving a crossword puzzle
As written, `asyncConcurrent(...)` is confusing as shit, and unless you memorize this blog post, you'll have no idea what this code means. I get that Zig (like Rust, which I really like fwiw) is trying all kinds of new hipster things, but half the time they just end up being unintuitive and confusing. Either implement (async-based) commutativity/operation ordering somehow (like Rust's lifetimes maybe?) or just use what people are already used to.