I've spent the last year working on an async runtime for Zig and I really grew fond of stackful coroutines. Your just program your code as if everything was blocking. The main benefit is that you can use whatever library, it doesn't have to be async aware. Heck, I could even use C libraries, and they would work correctly with my coroutines. I really don't understand why GC-based languages decided to go with stackless…
> I really don't understand why GC-based languages decided to go with stackless coroutines From what I've read it's due to a general idea that stacks use a lot of memory, which limits how many of them can be spawned. It's only good if it scales to a million concurrent users. A million 16 KiB stacks is over 16 GiB.
What color is your function? (2015)
171–180 of 198 posts
Re: What color is your function? (2015)
#172I've spent the last year working on an async runtime for Zig and I really grew fond of stackful coroutines. Your just program your code as if everything was blocking. The main benefit is that you can use whatever library, it doesn't have to be async aware. Heck, I could even use C libraries, and they would work correctly with my coroutines. I really don't understand why GC-based languages decided to go with stackless…
What are your thoughts on the Io situation? Has your work been with the Io plans in mind from the beginning?
There are many shortcomings in the API, especially missing timeouts everywhere. It was clearly designed by people working on the compiler and other local tools, and not much network services, but I think it's a great improvement in the Zig ecosystem and hopefully people can now write reusable libraries.
Re: What color is your function? (2015)
#173Earlier quoted context omitted.
This is a subtle point that I've seen missed repeatedly, but: The reason that "color" is important is that if you have a function ten layers down in your stack that is the wrong "color", you now have to change that top-level function. There is no other option. Propagating errors up the stack is not the same, because the top-level function is not developing an error return because of the 10-level-nested function. It i…
> Propagating errors up the stack is not the same, because the top-level function is not developing an error return because of the 10-level-nested function. It is developing one because the function it called has one, and apparently, it needs to return it to its local caller. It's a local consideration ... > By contrast, in a function coloring situation, if the color is wrong 10 layers down, you must change the calli…
You really can't. Shutting down the async loop doesn't just do damage to the performance of the program, it can actually affect correctness. It doesn't fix the color problem. That's why I said 'Although I will say that if your "encapsulation" is basically to run it in a non-concurrent environment, that's really not encapsulation. It isn't really "encapsulation" if you're giving up an entire major feature of the language, because that is something very visible to the rest of the program.'
Monads actually aren't really relevant, either. A monad can express something you can't escape from, but it isn't required; "Option" isn't a color because you can still deconstruct it any time you like. It's specifically IO, which traps you not because it is a "monad" but because it has no escape hatches at all. (Modulo "unsafe", which is always something we have to say, but we also always tend to ignore unsafe in these discussions because otherwise everything collapses to one big unsafe pile in all languages of note.)
Re: What color is your function? (2015)
#174Earlier quoted context omitted.
This is a subtle point that I've seen missed repeatedly, but: The reason that "color" is important is that if you have a function ten layers down in your stack that is the wrong "color", you now have to change that top-level function. There is no other option. Propagating errors up the stack is not the same, because the top-level function is not developing an error return because of the 10-level-nested function. It i…
That's just not true. Let's say you have a form validation library with a public api that supports custom validators Validate(name string, value string) bool. Then you decide that your validator now needs to make an HTTP request. This request needs context so that tracing is propagated and needs to return (bool, error) so that error is propagated up instead of silently ignoring it or logging it and returning false. T…
The color concept is interesting precisely because it isn't just "arguments to functions". The difference I describe is a real one that has real effects at scale. Trying to collapse it down to "it's just function arguments" doesn't make it go away, it just makes it so anyone who refuses to draw the distinction loses the ability to see it. It's not a good idea and it's not a good argument. You're just smearing vaseline on your eyes.
Re: What color is your function? (2015)
#175Earlier quoted context omitted.
> The problem with function color exists when you can't abstract over it Hopefully it's safe read this as there's no common static type between function and async function meaning APIs (that take functions as arguments) have to provide seperate methods (or overloading) for these different colours. Like in typescript you can write ` (f: () => T) => T` because an async function statically is just the return type wrappe…
Your first paragraph links having the colour in the type system as allowing you to write functions that take arguments of parametric colour; your last paragraph says you're unconvinced that you might also like to write functions that return results of parametric colour. An example: a vector of things to a thing of a vector, for "thing" in (promise, option, result , ...). Such a function should only really return a pr…
Anyways, the two points:
- The first point was, "not having a common way to generalise over both sync, async or blue, green, brown functions, seems avoidable and bad". This is when the type system struggling to common up with a common classification for function invocation independently of colour.
- The second point was that, was "so what if there are different return / wrapping / container / monad types", which focuses on a more common interpretation of this article but a different one.
In Haskell a type in a result, State, Config, Parsec, Maybe is in it for a reason, and thankfully we can generalise over that. Higher kind types (abstracting over abstractions) is a whole other basket, as an ex haskeller I would love to see them more mainstream but admittedly I don't think language authors are convinced and there isn't much we can do about it, so we should learn to make do with what we have outside of haskell.
Re: What color is your function? (2015)
#176Earlier quoted context omitted.
> I really don't understand why GC-based languages decided to go with stackless coroutines From what I've read it's due to a general idea that stacks use a lot of memory, which limits how many of them can be spawned. It's only good if it scales to a million concurrent users. A million 16 KiB stacks is over 16 GiB.
But if you have stack growth, the way Go does it, then the stack you allocate is actual memory you use. You are just trading heap allocations for stack growth. I started to see the stack as a super fast allocator that is always available.
I think the popularity of event loops stems from the fact Node does it. Pretty much cooperative multitasking by another name. Functions are coroutines, return is yield and the event loop is the scheduler.
Re: What color is your function? (2015)
#177Earlier quoted context omitted.
Java just makes them hard to use. They're not fully apart of the type system and they're hard to escape when you actually want to panic. Everyone around here praises Rust's result, checked exceptions are the same idea: fn someFn() -> Result T someFn() throws E fun someFn(): T | E // Kotlin's proposed error unions Checked exceptions actually compose a little better when you have a function that can throw multiple type…
> // dunno panic This is exactly why Java is such a pain to work with. Somehow, Java developers all decided to stop dealing with error conditions and just crash the stack whenever something weird happens. The way Java developers seem to work these days has a lot in common with Rust beginners that just `?` or `.unwrap()` every single fallible method. Random crashes ("RuntimeException") are acceptable, so nobody even b…
Re: What color is your function? (2015)
#178Earlier quoted context omitted.
Result types do have one problem that checked exceptions don’t. Checked exceptions automatically combine into union types in a throws or catch clause. I haven’t seen a language that lets you be generic like that. T fn() throws E, F, G vs Result // not even Rust lets you do this.
That's more a consequence of Rust needing its tagged unions declared up front so it can lay them out consistently in memory without runtime type information. Python and TypeScript have untagged unions (that are discriminated at runtime by the RTTI attached to all objects in the underlying dynamic language); they don't happen to have an equivalent of Rust's ? operator, but if they did it'd work like you're describing.
(Rust has both tagged (enum) and untagged (union) unions, but untagged ones are unsafe and therefore mostly used for C interop and similar cases.)
Re: What color is your function? (2015)
#179Earlier quoted context omitted.
The problem with checked exceptions is that they don't compose with the rest of the type system. Hence the infamous problems with things like Streams. Result types have basically all the virtues of checked exceptions without the problems.
Result types do have one problem that checked exceptions don’t. Checked exceptions automatically combine into union types in a throws or catch clause. I haven’t seen a language that lets you be generic like that. T fn() throws E, F, G vs Result // not even Rust lets you do this.
TypeScript is an example of a language with union types: https://www.typescriptlang.org/docs/handbook/unions-and-inte...
Re: What color is your function? (2015)
#180Earlier quoted context omitted.
This is a subtle point that I've seen missed repeatedly, but: The reason that "color" is important is that if you have a function ten layers down in your stack that is the wrong "color", you now have to change that top-level function. There is no other option. Propagating errors up the stack is not the same, because the top-level function is not developing an error return because of the 10-level-nested function. It i…
That's just not true. Let's say you have a form validation library with a public api that supports custom validators Validate(name string, value string) bool. Then you decide that your validator now needs to make an HTTP request. This request needs context so that tracing is propagated and needs to return (bool, error) so that error is propagated up instead of silently ignoring it or logging it and returning false. T…
With stackful coroutines, the custom validator can transport the error to the original call site, handle it and resume back into the validation library if the error was recovered, or abandon the validation (as with exceptions) if unrecoverable.
With HKTs, the validation library can be agnostic on the specific return type of the custom validator.