6 ms·
"Asynchrony" is a very bad word for this and we already have a very well-defined mathematical one: commutativity. Some operations are commutative (order does no
by dvt 1y ago
"Asynchrony" is a very bad word for this and we already have a very well-defined mathematical one: commutativity. Some operations are commutative (order does not matter: addition, multiplication, etc.), while others are non-commutative (order does matter: subtraction, division, etc.).
try io.asyncConcurrent(Server.accept, .{server, io});
io.async(Cient.connect, .{client, io});
Usually, ordering of operations in code is indicated by the line number (first line happens before the second line, and so on), but I understand that this might fly out the window in async code. So, my gut tells me this would be better achieved with the (shudder) `.then(...)` paradigm. It sucks, but better the devil you know than the devil you don't.
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.
- ryandv 1y agoStrictly speaking commutativity is defined over (binary) operations - so if one were to say that two async statements (e.g. connect/accept) are commutative, I would have to ask, "under what operation?" 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.
- dvt 1y agoCommutative operations (all of them I think?) are trivially generalized to n-ary operations (in fact, we do this via ∑ and ∏, in the case of addition and multiplication, respectively). You're right that the question of what "operation" we're dealing with here is a bit hazy; but I'd wager that it's probably in the family of the increment operation (N++ === N + 1 = 1 + N) since we're constantly evaluating the next line of code, like the head of a Turing machine. 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
- senderista 1y agoGeneralizing an associative binary op to an n-ary op just requires an identity element Id (which isn't always obvious, e.g. Id_AND=true but Id_OR=false).
- singularity2001 1y agoIdentity is nop / pass
- JadeNB 1y ago> Generalizing an associative binary op to an n-ary op just requires an identity element Id (which isn't always obvious, e.g. Id_AND=true but Id_OR=false). 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.")
- dwattttt 1y agoImplication sounds right. With no further analysis, running each line in order is correct (for whatever "order" is defined by a language, let's assume imperative). 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.
- benreesman 1y agoIt's a good intuition. This has been studied extensively, the composition rule that is lax enough to permit arbitrary effects but strict enough to guarantee this class of outcomes is (>>=). We can keep trying to cheat this as long as we want, but it's bind.
- xscott 1y ago> "under what operation?" 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.
- ryandv 1y agoRight, exactly. It's been said that (>>=) is a programmable semicolon. [0] https://news.ycombinator.com/item?id=21715426 https://news.ycombinator.com/item?id=21715426
- singularity2001 1y agoor carrots return/new line For that matter
- noduerme 1y ago`.then()` is ugly, `await` is pretty, but wouldn't the critical part to guarantee commutivity less than guaranteed order (in js) be the `Promise.all([])` part?
- Nevermark 1y agoThe "operator" in this case would be the CPU executing 2 or N procedures (or functions). 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.
- jhanschoo 1y agoUnder function composition `;`, where both the LHS and RHS are viewed as functions operating on the whole environment state.
- ryandv 1y agoRight; though it's a special kind of function composition (Kleisli composition) and often presented in a different form (bind, >>=).
- dooglius 1y agoCommutativity is a much weaker claim because one is totally before or after the other. e.g. AB may commute with C so ABC=CAB but it is not necessarily the case that this equals ACB. With asynchrony you are guaranteed ABC=ACB=CAB. (There may be an exisiting mathematical term for this but I don't know it)
- dvt 1y agoYou can prove three-term commutativity from two-term (I did it years ago, I think it looked something like this[1]), so the ordering doesn't matter. [1] https://math.stackexchange.com/questions/785576/prove-the-commutativity-property-of-addition-of-natural-numbers-by-induction https://math.stackexchange.com/questions/785576/prove-the-co...
- Ar-Curunir 1y agoStrictly speaking this also requires associativity.
- dooglius 1y agoI'm not talking about a universe where all elements commute, I'm talking about a situation in which A, B, and C do not necessarily commute but (AB) and C do. For a rigorous definition: given X and Y from some semigroup G, say X and Y are asynchronous if for any finite decompositions X=Z_{a_1}Z_{a_2}...Z_{a_n} and Y=Z_{b_1}Z_{b_2}...Z_{b_m} (with Z's in G) then for any permutation c_1,...,c_{n+m} of a_1,...,a_n,b_1,...,b_m that preserves the ordering of a's and the ordering of the b's has XY=Z_{c_1}Z_{c_2}...Z_{c_{n+m}}. I make the following claim: if G is commutative then all elements are asynchronous, but for a noncommutative G there can exist elements X and Y that commute (i.e. XY=YX) but X and Y are not asynchronous.
- JW_00000 1y agoTo give a concrete example, matrix multiplication is not commutative in general (AB ≠ BA), but e.g. multiplication with the identity matrix is (AI = IA). So AIB = ABI ≠ BAI. 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.
- brailsafe 1y ago> "Asynchrony" is a very bad word for this and we already have a very well-defined mathematical one: commutativity. 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
- throwawaymaths 1y agocommutativity is also not correct, because 1) it means way more things than just temporal ordering and 2) there are cooky temporal ordering schemes you can come up with (interleaving multiple async/awaits in weird time-dependent ways) which aren't really describable in the simple mathematical notion of commutativity.
- deleted 1y ago[deleted]
- hinkley 1y agoAsynchrony also allows for partial ordering. Two operations may still need to be retired in a particular order without having to execute in that order. 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.
- ordu 1y ago> 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. 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.
- psychoslave 1y agoI'm not sure they are that different, you could just as well store function calls in some constant each on its line then addition the result on a third. This is only syntax, not conceptual difference here. And on practical level, the difference is that the operator can be directly matched with some machine instruction, with operands being native data type such as integer. Still, you might then prefer a word as permutability, or swappability.
- delusional 1y ago> Usually, ordering of operations in code is indicated by the line number 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.
- tbrownaw 1y ago> Some operations are commutative (order does not matter: addition, multiplication, etc.) Fun fact: order does matter for addition. (When adding many floating-point numbers with widely varying exponents.)
- tsimionescu 1y ago> So, my gut tells me this would be better achieved with the (shudder) `.then(...)` paradigm. It sucks, but better the devil you know than the devil you don't. 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.
- Yoric 1y agoCan confirm. 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.
- psychoslave 1y agoHow is that any better to have await? Any resources I might consult on this?
- Yoric 1y agoWell, one of the ways we "sold" async/await it to Google was by showing how we could improve Promise-based tests. 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`.
- tsimionescu 1y agoWell, consider the difference between 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.
- alfiedotwtf 1y ago> Usually, ordering of operations in code is indicated by the line number (first line happens before the second line, and so on), but I understand that this might fly out the window in async code 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
- sdbrady 1y agoI'm not sure what you mean by "statements" in Prolog as it's not a term the language defines. If you're referring to clauses, it's not true that execution is unordered: the Prolog interpreter attempts to unify a goal with clauses from the knowledge base in the order they appear. This ordering is semantically significant for control flow. 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.
- alfiedotwtf 1y ago> the Prolog interpreter attempts to unify a goal with clauses from the knowledge base in the order they appear. 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