"Because what you wrote isn't a promise: it's a condition variable."
Yes, I'm sort of cheating, because what the original blog post implemented isn't really promises either. It's hard to "really" implement promises in Go because whereas in Javascript you're implementing a different "color" language, in Go you're simply re-implementing the same color again. (And quite poorly, given Go's very weak/simple type system.) If you do good software engineering and remove redundancy, promises simply collapse down to conditions, or disappear entirely.
I've tweaked my post to clarify I'm propagating the original post's usage.
The point I'm really shooting for is that you can avoid a significant amount of, but not all of, interface{} usage when you're trying to use something like that Promise by writing the logic in an object, then composing that in to another type-safe struct. You can not, of course, implement true generic containers that way.
I dislike not having true generics in Go, but I have found I like the composition instead of inheritance default quite a bit, once I adjusted. I think there's room for another new language to adopt that default and make some progress in spaces where Go simply refuses to play.
Re: threads, I agree completely. What I care about is having a stack, so I can do structured programming even in my "async" code, not the hardware representation of the threads. Really need to finish up my post on that someday.
> What I care about is having a stack, so I can do structured programming even in my "async" code, not the hardware representation of the threads. Really need to finish up my post on that someday.
When you write that post, make sure you talk about performance. :)
In particular, there is always a performance cost to having a stack, and not having one is much of the reason why nginx is so fast. nginx could not switch to a goroutine-like model without losing performance. There is no free lunch; threaded programming (goroutines, etc) is better for developer ergonomics, while stackless/state machine programming is better for performance.
I'm not planning on putting it in there, because it's one of those things that programmers use as an excuse. "Oh, I can't afford a stack, I need my code to go quickly. Even if it takes me longer to write. And crashes more often. And is harder to debug. And I can't use half my language features. And I'm just writing CRUD apps for my bank."
It's a last-ditch optimization for when you've got nothing else left, or perhaps in your case, something that a language designer worries about. But at the moment, I see a lot more people grossly underestimating the costs of throwing away the stack than underestimating the benefits. It's a rare program that has been so hyperoptimized that there's nothing left to do but throw away the stack, and even if you get that far, you need to understand the full import of what that means, which is actually a great deal more than just "oh, I don't have these bytes in RAM anymore".
I completely disagree. :) Stacks have a large performance cost when you get to this the last level of optimization; we're talking 2x or more in tight syscall loops when you take cache misses into account. Moreover, the memory costs of stacks compared to the most optimized state machine can easily be 10x. You may not need this level of performance, but some people do.
This leads me to the exact opposite conclusion that you have. I think that, in terms of language design, we should be figuring out how to design stackless coroutines in a way that is as ergonomic as the stackful ones instead of throwing up our hands and saying "we're going to eat this large performance cost because there's no way we can possibly design a language that makes stackless programming ergonomic and robust".
As language designers, we shouldn't be giving up before even fighting the battle. Because, if we do, nginx will be around and winning the performance fight, and people will (rightly in many cases) never switch away from it, and that old-timey C will persist forever. My former colleague Robert O'Callahan had a great view on "abstraction taxes": if your abstractions aren't zero-cost, you're incentivizing programmers to not use them, and you're forcing programmers to choose between good engineering practice and good performance.
Yes, I'm sort of cheating, because what the original blog post implemented isn't really promises either. It's hard to "really" implement promises in Go because whereas in Javascript you're implementing a different "color" language, in Go you're simply re-implementing the same color again. (And quite poorly, given Go's very weak/simple type system.) If you do good software engineering and remove redundancy, promises simply collapse down to conditions, or disappear entirely.
I've tweaked my post to clarify I'm propagating the original post's usage.
The point I'm really shooting for is that you can avoid a significant amount of, but not all of, interface{} usage when you're trying to use something like that Promise by writing the logic in an object, then composing that in to another type-safe struct. You can not, of course, implement true generic containers that way.
I dislike not having true generics in Go, but I have found I like the composition instead of inheritance default quite a bit, once I adjusted. I think there's room for another new language to adopt that default and make some progress in spaces where Go simply refuses to play.
Re: threads, I agree completely. What I care about is having a stack, so I can do structured programming even in my "async" code, not the hardware representation of the threads. Really need to finish up my post on that someday.