Live data from Hacker News

What color is your function? (2015)

journal.stuffwithstuff.com

131–140 of 198 posts

Re: What color is your function? (2015)

#131

Earlier quoted context omitted.

What I like least about this article is that it's completely soured the entire context of asynchronous programming. Invariably, any time someone discusses design of an async functionality, function coloring is brought up, with almost no analysis as to how it applies and why it's a good or bad thing. (Ironically, I probably see more in-depth analysis these days as to why this article isn't apropos than why it is when…

I am so happy I have never heard anyone IRL say colored functions. It would annoy me. The concept is interesting but like all engineering it is a trade off. In Node amd Go you don't get a choice anyway. In C# you might choose based on performance thinking of thread pools etc IIRC. When programming in Node I find in practice async and "colored functions" no issue especially with async await. Except for performance iss…

The Node world was built with asynchronicity in mind. First via callbacks, then Promises, then async/await (Promise-based), so it feels natural now.

But if you take Python (for example), it's a shitshow. You usually have two versions of the same API, split by function name, client, package, or namespace: `foo` and `afoo`, where the a-prefixed one is async and meant to be used inside async function call chains, and the other one is the blocking version for non-async chains (which are still very much in use). It's a pain to develop for, to maintain, to scale, everything.

Re: What color is your function? (2015)

#132

Earlier quoted context omitted.

> The functions are still coloured, just implicitly. IYKYK to spawn a goroutine or not ts. In Go, you can choose to either block on a function call or to execute it as a go routine. The function has no "color" in the sense of the article. If you want to print asynchronously, you can with a `go fmt.Println("Hello")`, or you can block on that print and remove the `go `. There is no color to any function. And the functi…

Say you have a function like validate_user(). In Go, should you block the main thread on this call, or fork and join? What if it makes a DB request and now your UI is blocked for 2 seconds or something? You need to know implicitly you need to call validate_user() in a goroutine, and then deal with forking and joining manually. If it's explicitly coloured as async, you know. In most async/await languages you can run a…

I think you're missing something. Go's functions are all blue, not all red. As far as I know, the callers of async functions with Rust's Tokio and C#'s Tasks themselves need to be async; not the case for Go.

Concurrency is usually a mix of goroutines and channels. There is no inherent link between caller and asynchronous callee. You can use goroutines without channels, and channels without goroutines.

You can write "go" to launch any function call in its own goroutine, but you cannot get a return value from it. This isn't valid:

    user_is_valid := go validate_user(u)
The idiomatic way you can do that is to use a goroutine and a channel:

    ch := make(chan bool)
    go func() {                  // runs asynchronously
        ch 
I don't think it's a boon to have functions "coloured for you" to tell you that they might block. On the other hand, functions that would block tend to accept a Context parameter to let you control what they should do. It's a major indicator that the function's probably going to do something async, but it doesn't have to.

Re: What color is your function? (2015)

#133
post #65

Earlier quoted context omitted.

#3 is not satisfied, as you noted in #2. You can call `throws` methods from non-`throws` methods by wrapping the call in a try catch, and `throws` methods can call non-`throws`. There isn't an exclusivity asymmetry like there is for JavaScript async.

That only applies to Javascript, which, is mostly only red functions anyways (there are no blocking apis in javascript). Javascript doesn't have the coloring problem in the way Python or Rust has it. In Python, you can wrap the call with asyncio.to_thread, in rust with tokio::spawn_blocking.

I think the big difference is that in an application that cares about throughput and being non-blocking simply blocking isn’t really possible as it affects the whole performance of the application. Wrapping a checked exception in an unchecked one doesn’t do that.

Re: What color is your function? (2015)

#134
post #70

Earlier quoted context omitted.

Ok, sorry it's been about 20 years since I last javad IIRC you didn't have to declare exceptions in your function signatures. However, wrapping in try/catch seems to violate #3. Try catch is not a heavy lift of a seam between red and blue To be fair, #3 seems to have shades of grey. In some pls, you can call an async function from a sync one by wrapping it in a whole damn event loop system. Should that count?

> Ok, sorry it's been about 20 years since I last javad and you didn't have to declare exceptions in your function signatures. You're probably remembering RuntimeExceptions, which are a subgroup [0] that are exempt from "checking" by the compiler, which means it does not require method signatures to declare "I might emit this." [0] https://docs.oracle.com/en/java/javase/26/docs/api/java.base...

You can declare your runtime exceptions too. The compiler won’t enforce you to catch them though.

    void fn() throws IllegalStateException

Re: What color is your function? (2015)

#135
post #66

Earlier quoted context omitted.

> Like async, the biggest problem with exceptions were the ergonomics. I know it's not a popular take, but I prefer the idea of Checked Exceptions over unchecked ones [0], and suspect current opinions would be vastly different if Java had shipped with some sweet syntactic sugar for: "If an exception that is of kind A or B or C occurs, automatically throw another checked exception X with the original exception as a ca…

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.

Re: What color is your function? (2015)

#136

Earlier quoted context omitted.

Ok, sorry it's been about 20 years since I last javad IIRC you didn't have to declare exceptions in your function signatures. However, wrapping in try/catch seems to violate #3. Try catch is not a heavy lift of a seam between red and blue To be fair, #3 seems to have shades of grey. In some pls, you can call an async function from a sync one by wrapping it in a whole damn event loop system. Should that count?

Your question just kicks the can down the road. The problem with the article, to me, is the author doesn't want to accept a certain amount of complexity and has erected arbitrary road blocks. To you, a "whole damn event loop system" is too high a price to pay, but try/catch is not. The complexity of exceptions is invisible to you. However there are certain environments (e.g. FFI) where I dont want "the whole damn exc…

Maybe author do not want to hide the complexity in functions?

Re: What color is your function? (2015)

#137
post #36
post #16

Colored functions are good. It reflects the language design on signaling what is important, and what are the properties it want the writer to pay attention. Other examples of colored functions: * Haskell: pure function and non-pure (IO monads) looks different. * Rust: unsafe functions (or block) requires special markers.

Rust unsafe functions aren't a good example of colored functions because it doesn't exhibit the main issue brought up in the article, that one color can call the other but not the other way around. In Rust, unsafe code can call safe code, and safe code can call unsafe code. Calling unsafe code in safe code requires an explicit unsafe block, but that's fairly normal and not a hack to get around function coloring. A be…

Rust's sync functions can block and await async functions.

Which is another problem with the article: it doesn't clearly define what counts as having the "color". The problematic dead-end situation exists in JS, but languages with cross-thread communication can work around it.

Re: What color is your function? (2015)

#138

Earlier quoted context omitted.

Say you have a function like validate_user(). In Go, should you block the main thread on this call, or fork and join? What if it makes a DB request and now your UI is blocked for 2 seconds or something? You need to know implicitly you need to call validate_user() in a goroutine, and then deal with forking and joining manually. If it's explicitly coloured as async, you know. In most async/await languages you can run a…

I think you're missing something. Go's functions are all blue , not all red. As far as I know, the callers of async functions with Rust's Tokio and C#'s Tasks themselves need to be async; not the case for Go. Concurrency is usually a mix of goroutines and channels. There is no inherent link between caller and asynchronous callee. You can use goroutines without channels, and channels without goroutines. You can write…

Well, the benefit of async/await is that you can just do

   let user_is_valid = validate_user(u).await
But you can also pass around future values. It's pretty dang ergonomic, the tradeoff is that it requires more ceremony to block on async functions (and is not even possible in JS). This was considered a potential problem 10 years ago but we've discovered since than that it's not really an issue at all.

My point about the colouring is that it's actually nice to have explicit colouring, in go the asynchronicity of functions aren't encoded by the type system but in practice you still handle them differently. You can't just call one of them without passing in things like context, or handling channels without refactoring anyways.

Re: What color is your function? (2015)

#139
post #92

Earlier quoted context omitted.

The problem with Java's checked exceptions is that it has too many kinds of exceptions to choose from and they're overly specific. Compare with Go, which has a single error interface and had it from the beginning, so it's used everywhere. Returning a new kind of error is always a local change, unless it's a function that didn't previously report errors at all. Type systems permit either standardization or fragmentati…

> too many kinds of exceptions to choose from I don't understand, why would you need to pick a checked exception? It's the dual or mirror of feeling paralyzed over a return-type because there are "too many kinds of Object to choose from." If you're writing a CrystalBall class with a gaze_deeply() method, you'll probably return your own VisionResult (extends Object) unless it throws your TooCloudedException (extends E…

> you'll probably return your own VisionResult (extends Object) unless it throws your TooCloudedException (extends Exception).

> When someone else writes a wrapper or higher-level layer that uses your code, then it'll be up to them to convert or wrap those results and exceptions into something suitable for their level of abstraction.

Why though? What do you gain other than longer stacktraces with all those wrappers? People always trot out some theoretical notion that a caller is going to catch that framework's different exceptions and handle them differently, but have you ever seen calling code that actually did that?

> In other words, suppose all Java methods always returned Object [0]. That would also ensure that a new return type is "always a local change" to the compiler, but I think most developers would be rightly horrified if they came across code that worked that way.

There are many different kinds of values. There really aren't that many different kinds of error - there's "transient error that you might want to retry", "programmer called the API wrong", and that's about it, most other cases (like bad user input) probably shouldn't be exceptions.

Re: What color is your function? (2015)

#140

Earlier quoted context omitted.

Say I need the results from two expensive REST API calls, so I want to run them concurrently. Managing a thread pool you find a _better_ experience than one, two = await asyncio.gather(callOne(), callTwo()) ?

Doesn't Python support futures? with ThreadPoolExecutor() as executor: one, two = executor.map(lambda f: f(), [callOne, callTwo]) I'm sure you could write a nicer helper function that's more similar to gather as well.

Sure, in a web server context you'd probably want to instantiate that executor globally and re-use it, Then you could write helper functions around that. But it's still considerably more code and legwork than the async alternative, while also being slower and using more memory if you care about those things.
Post reply on HN