Live data from Hacker News

What color is your function? (2015)

journal.stuffwithstuff.com

161–170 of 198 posts

Re: What color is your function? (2015)

#161
post #30

I really don't like this article. It has a catchy, profound-sounding title that people bandy about to argue against stuff they don't like. All functions, even non-async functions, are colored. In any large system codebase you'll have functions that can only be called in certain situations, with the right setup, whatever, and if you're lucky this is communicated by types but regardless those restrictions can't be avoi…

I agree it is a dumb article. It made a big fuzz back then but to me it's excellent as a litmus test. Learning async grammar in Javascript takes you an afternoon, after a week it should become familiar. If someone is unable to grasp that ... well, that tells you where they stand.

> I agree it is a dumb article.

It is a great article. Just because you misinterpret it doesn’t make it dumb.

Re: What color is your function? (2015)

#162

Earlier quoted context omitted.

what I like least about this article is how people seem to just substitute some other, usually theory, notion of what function coloring is to elide the argument (usually to excuse their favorite PL) without actually RTFA. The article is about ergonomics, not PL theory. > Exceptions cause function coloring do they? Do they? 1) Every function has a color 2) The way you call a function depends on its color 3) You can on…

Java's checked exceptions fit the 5 criteria: 1. It either `throws` or it doesn't 2. If the function `throws` you have to wrap it in try/catch, or make your function `throws` 3. Your function is `red` if it `throws` the same exception. 4. see (2) 5. See the FileReader class in core. Now, C++ exceptions might not satisfy all of these, but the problems CheckedExceptions were meant to solve still exist in C++ and as a r…

Exceptions are a very good comparison because they also perform non-local control flow.

Checked exceptions are a form of coloring, while unchecked aren't. But Java (which has checked exceptions) has an escape into unchecked land in the form of RuntimeError, most async languages do not, short of spawning a background thread (for sync->async) or force blocking (async->sync).

Interestingly, Result based error models are semantically (and even syntactically, mostly, except for the call-site annotation) equivalent to checked exceptions. Usually these languages have enough abstraction capabilities (HKT for example) to make coloring not an issue, or, again, an escape into unchecked land (for example panic in rust or Go, although the latter hardly counts as having Result-like error handling).

Re: What color is your function? (2015)

#163
post #152

Earlier quoted context omitted.

I don’t really see how declaring an unsafe block is materially different for the purposes of this discussion than, e.g., entering an async runtime. It is code you need to write, tradeoffs you need to weigh, invariants you need to keep.

In Rust, if you have a function containing an unsafe block, you do not need to use another unsafe block to call the function. Therefore, unsafe is not “contagious” like JavaScript’s async.

> you do not need to use another unsafe block to call the function

And in C#, you can just type `await` and call an async function from a sync function.

Calling unsafe requires an unsafe block from safe functions. That's essentially the same thing as async/await in many languages (Rust does things differently, of course, but that's even worse in my opinion).

Re: What color is your function? (2015)

#164
post #71
post #27

Earlier quoted context omitted.

The problem with checked exceptions afaik was far more in the execution than in the idea itself. And also late 90s-early 00s was different time in general.

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 bothers doing error handling any more.

Even the base SDK doesn't really bother with handling exceptions (i.e. the story with streams + exceptions). It's an excellent language feature tainted by a combination of bad choices twenty years ago and a weird culture shift in error handling.

Re: What color is your function? (2015)

#165
post #152

Earlier quoted context omitted.

In Rust, if you have a function containing an unsafe block, you do not need to use another unsafe block to call the function. Therefore, unsafe is not “contagious” like JavaScript’s async.

> you do not need to use another unsafe block to call the function And in C#, you can just type `await` and call an async function from a sync function. Calling unsafe requires an unsafe block from safe functions. That's essentially the same thing as async/await in many languages (Rust does things differently, of course, but that's even worse in my opinion).

> And in C#, you can just type `await` and call an async function from a sync function.

Yes, but not in JavaScript.

Re: What color is your function? (2015)

#166
post #139
post #92

Earlier quoted context omitted.

> 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?…

> 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?

You've never seen a try that has more than 1 catch block for different exception types?

> 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.

Do you think bad user input should be a result type? Because exceptions are essentially the same thing.

You've hit on a couple of problems with exceptions in Java though. The first is I think the default for checked exceptions should be no stack trace. As the designer of the method you've left it to the caller to decide to handle it or not, if they choose to turn it into an unchecked exception then I believe that is where the stack trace should start from. Assuming there's enough context in the checked exception the designer of the method gave you everything you needed to handle it so why do we need to capture that part of the stack trace? If it ends up getting logged the source of the issue was where it was changed to an unchecked exception.

The other issue comes down to usability. Try catch blocks aren't expressions so if you want to default something in the case of a checked exception it's a lot of low information density lines. Converting to an unchecked exception is also more ceremony than it really needs to be, but there's not really a reason why it couldn't be made simpler with some syntax sugar.

Re: What color is your function? (2015)

#167
Edited because I didn't realize how old the article is.

Almost everything the article says about C# is wrong:

> Sync functions are just called, async ones need an await.

No they don't. An async method can be called like any other method and you'll just get the Task object.

> You can’t unwrap it unless you make your function async and await it.

False. You can just access .Result and it'll execute synchronously.

> Before they added async-await and all of the Task stuff, you just used regular sync API calls.

You can still call the sync APIs, and I'm not getting any impression that those APIs are unmaintained or that they're avoiding adding new ones where it makes sense, even if in many cases it's literally just a matter of calling the async method and accessing .Result!

When they describe how the compiler transforms async code into closures, they seem to get this right, but for some reason they treat it as a problem and I can't figure out why. The compiler transformation is exactly the right tool for the job. They even mention the great benefit of it not needing special runtime support. I'm very confused as to the direction of this argument.

By the way, while I was reading the first part, before the “big reveal”, my two top guesses for what it's about were C++ const correctness, and Java checked exceptions. I'd argue that more of their arguments apply to those than to async/await.

Re: What color is your function? (2015)

#168
post #10

I wish the key word was instead dontawait and was used inversely to how await is used. 99% of the time I'm using an async function, despite however slow it is, there's nothing for my code to do but wait for it to finish. But if for some reason I would like the next line of code to run before the current one is done, I'll let you know . Like, why can't my sync function await something asynchronous? If it has to lock u…

> Like, why can't my sync function await something asynchronous?

It can in C# (just call .Result). I'm not sure why other async/await languages, like JS, don't just add that too.

Re: What color is your function? (2015)

#169
post #167

Edited because I didn't realize how old the article is. Almost everything the article says about C# is wrong: > Sync functions are just called, async ones need an await. No they don't. An async method can be called like any other method and you'll just get the Task object. > You can’t unwrap it unless you make your function async and await it. False. You can just access .Result and it'll execute synchronously. > Befo…

async/await didn't hit javascript until 2017. in 2015 you picked between

function foo(cb) { cb(42, null) }

or

function foo() { return new Promise((resolve, reject) =>{ resolve(42) }) }

the second allowed for .then and .catch

foo.then((value) => console.log(value)).catch((error) => // handler error)

which isn't much better but did get us chaining

foo().then((v) => bar(v)).then((v) => baz(v)).catch()

Re: What color is your function? (2015)

#170

Earlier quoted context omitted.

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 t…

I think we're just seeing different sides of the same coin. Go's idiomatic code would be:

    user_is_valid := validate_user(u)
i.e. you have no special handling needed, it fits anywhere. You don't even know async is or isn't involved, and it wouldn't matter for correctness if it did. If you want to make it async, you get to do that yourself.

However, the evolution of that design has resulted in use of shared and nested Contexts in order to coordinate state and lifespans across goroutines, which is why they expose this parameter. And if you don't care, you can always use context.Background() for the value. Not only does it provide that synchronisation, but also it made cancellation and timeouts simple and standardised (obviously, async frameworks of other languages have their own idioms for this)

Post reply on HN