Live data from Hacker News

What color is your function? (2015)

journal.stuffwithstuff.com

191–198 of 198 posts

Re: What color is your function? (2015)

#191
post #139

Earlier quoted context omitted.

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

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

I've seen it for APIs that throw exceptions for bad input or whatever. But what I've never seen is more than one catch block for wrapper exceptions (except perhaps to unwrap the cause), where the calling code handles FrameworkNetworkError differently from FrameworkDatabaseError or what have you.

> Do you think bad user input should be a result type?

Yes

> Because exceptions are essentially the same thing.

Well, except for all the ways they're not that you mentioned in the following paragraphs.

Re: What color is your function? (2015)

#192
post #186
post #139

Earlier quoted context omitted.

> 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? I'm not sure if this means: 1. "Why bother throwing a new exception of a different class, and not bubble up the original as-is?" 2. "Why would you use the standard feature of all Java exceptions which allows you to chain them, and not just throw away the original exception after copying some of its message string?" For #1, it should be obvious in almost any language, if not instinctive: The library for…

> It's a matter of abstraction and preventing weird coupling.

Does that actually work though? Who ever handles CustomerAccessException specifically, except by calling getCause() and looking at the underlying DatabaseException?

> There are many errors because all errors are contextual! Bad-input in the HTML form-submit is not the same as bad-input in the SQL query. A failed invariant of a tree that somehow made a loop is not the same as a failed invariant of something reporting negative length. An SSH handshake error is not a TLS handshake error.

But the way you handle them is the same. All you can ever really do is a) retry it or b) fail it and alert the developer. And the wrapper doesn't make either of those any easier.

Re: What color is your function? (2015)

#193

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…

The problem with function color exists when you can't abstract over it[1] Some statically typed languages (I believe both Haskell, ocaml) have powerful type system that allow abstracting over function types and function colors. Color is not an issue here. Some other statically typed languages (C#, rust, and C++ (at least with the built-in stackless coroutines)) can abstract over types but not over colors. This is a p…

This is the kind of comment I come to HN for. Thanks for teaching me something.

Re: What color is your function? (2015)

#194
post #175

Earlier quoted context omitted.

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…

I think you're confused, I was talking to two different points, while I'm sure I could have communicated with more precision, either missed it, it was unclear or you don't understand, either way I don't really get the gotcha tone when you could ask for a clarification: 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 av…

I think I was simply not very good at expressing what I was trying to convey, sorry, and it is a fault of mine to come across as gotcha-y even when trying not to. Thank you for responding with patience despite that.

The first point I interpret as "colourful arguments are avoidable and bad", with which I agree.

The second point I interpret as "colourful returns are unavoidable but good", with which I disagree - even if that interpretation is too strong and is more "... are unavoidable".

A function's type is its full signature, including inputs and outputs. When you have first-class functions, you have values with function types, and those values are inputs to other functions. Necessarily, then, if you colour outputs you have also applied colour to inputs.

Transposing a vector of things to a thing of vectors is an example of where colourful output forces colourful input. If you cannot abstract over abstractions, you must write and re-write the sequence function for each abstraction.

I'm in agreement with your closing paragraph's sentiment. That HKTs aren't a broadly adopted solution is something I accept, but I reserve the right to low-key begrudge it.

(And the more I write about this, the more I wish the original article had used "flavour" rather than "colour" as I try and probably fail to find phrasing that doesn't simply sound like portions of a racist rant.)

Re: What color is your function? (2015)

#195
post #175

Earlier quoted context omitted.

I think you're confused, I was talking to two different points, while I'm sure I could have communicated with more precision, either missed it, it was unclear or you don't understand, either way I don't really get the gotcha tone when you could ask for a clarification: 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 av…

I think I was simply not very good at expressing what I was trying to convey, sorry, and it is a fault of mine to come across as gotcha-y even when trying not to. Thank you for responding with patience despite that. The first point I interpret as "colourful arguments are avoidable and bad", with which I agree. The second point I interpret as "colourful returns are unavoidable but good", with which I disagree - even i…

> I think I was simply not very good at expressing what I was trying to convey, sorry

No its, and I appreciate you taking the time to read my reply and consider my perspective here.

> The second point I interpret as "colourful returns are unavoidable but good", with which I disagree - even if that interpretation is too strong and is more "... are unavoidable"

Thats fair, but yeah I wouldn't go so far to say its good or imply we should celebrate it in anyways, more so it as a unavoidable constraint that warrants engaging with.

For sure HKT would generalise many stray ends, and there are definately more complicated usecases where you can write much nicer types with HKT, although my experience has been theres been more pain in writing them without HKT than using them without HKT so the pain is a fixed cost of building the library and not an on going problem of using it. Although I am sure there are cases where it's also the case that usage of the library is more painful without HKT.

IDK, I haven't written a ton of Haskell in a while maybe I've forgotten some of its magic and internalised some of the suboptimal aspects of the absense of working without HKT.

But I do from time to time find problems that would be nicer to solve with HKT, I think generally quite a few of them are DSLs or some form of meta programming. I guess in typescript conditional types you can get away with a lot cooked things.

> Transposing a vector of things to a thing of vectors is an example of where colourful output forces colourful input. If you cannot abstract over abstractions, you must write and re-write the sequence function for each abstraction

I've unfortunately also had similar issues from the lack of HKTs with some linear algebra APIs so I don't find this too surprising.

Re: What color is your function? (2015)

#196
post #192
post #186

Earlier quoted context omitted.

> Why though? I'm not sure if this means: 1. "Why bother throwing a new exception of a different class, and not bubble up the original as-is?" 2. "Why would you use the standard feature of all Java exceptions which allows you to chain them, and not just throw away the original exception after copying some of its message string?" For #1, it should be obvious in almost any language, if not instinctive: The library for…

> It's a matter of abstraction and preventing weird coupling. Does that actually work though? Who ever handles CustomerAccessException specifically, except by calling getCause() and looking at the underlying DatabaseException? > There are many errors because all errors are contextual! Bad-input in the HTML form-submit is not the same as bad-input in the SQL query. A failed invariant of a tree that somehow made a loop…

Note: This subthread has become about exceptions in general, rather than checked-vs-unchecked. I don't mind, but I wanted to make it explicit.

> Who ever handles CustomerAccessException specifically, except by calling getCause() and looking at the underlying DatabaseException?

Ideally you call a method like `getDetailFoo()`, and using the inherited `getCause()` is for hacky workarounds, like when you need a change in behavior much sooner than Customer classes will be changed to give you the information you need in a proper way.

As I said before about abstraction and coupling, the programmer here shouldn't need to know that SomeDatabase v.1.2.3 is an implementation detail of Customer. Their code shouldn't need to have a direct dependency on some.database.DatabaseException to compile either. Sure, a Java programmer can use runtime reflection instead... but at that point they should definitely be having second-thoughts about whether they're on a path to the Dark Side.

Regardless of how you write the catch-logic, the chained cause remains important for logging and diagnosis.

> But the way you handle them is the same. All you can ever really do is a) retry it or b) fail it and alert the developer. And the wrapper doesn't make either of those any easier.

I'm going to assume this is equivalent to: "All reactions to errors usually fall into a few broad descriptive categories, and encapsulating the original exception doesn't help the handler make precise decisions."

Does that sound right? Because my initial readings went in much less charitable directions like "the person throwing the exception knows better than you do about how you should handle it later" or "all retries are the same basic logic."

> All you can ever really do is a) retry it or b) fail it and alert the developer. And the wrapper doesn't make either of those any easier.

The wrapper is quite important. Imagine these layers exist:

  1. 1-5 different HTTP libraries used by...
  2. 5 different API clients for 5 different fortune cookie service, used by...
  3. A library that promises a wide and resilient range of fortunes by putting many sources (today, 5) together and semi-randomly picking between them.
  4. The website you're making that shows fortunes when people log in.
Do you want layer #1 exceptions to travel all the way to #4 as-is, so that you get an HTTP 500 and you won't actually know which remote service is at fault? (Without grepping the stacktrace, which is *ick*.)

No, because either #2 or #3 should be wrapping the HTTP exception in a new one that can carry that extra "which one did I randomly pick" information in a defined way.

Do you want a #2 exception to travel to #4 without changing, so that you have to write catch-clauses (or reflection) for 5 or more exceptions from the 5 different services, code that will be wrong when a version update turns it into 7?

No, because #2's job is to abstract those N services away and throw its own much set of exceptions to cover common cases.

So after all that I realize I didn't specifically address retry-vs-fail choices, but I think this still sufficient to show that layering is necessary.

Re: What color is your function? (2015)

#197

Earlier quoted context omitted.

Same with the BEAM languages like Erlang, Elixir, and Gleam. Though it still bothers me that they call their green threads "processes".

They are (lightweight) processes since they have no shared memory. Each process has its own stack and heap.

I've never thought about it that way, but that makes sense!

Re: What color is your function? (2015)

#198
post #196
post #192

Earlier quoted context omitted.

> It's a matter of abstraction and preventing weird coupling. Does that actually work though? Who ever handles CustomerAccessException specifically, except by calling getCause() and looking at the underlying DatabaseException? > There are many errors because all errors are contextual! Bad-input in the HTML form-submit is not the same as bad-input in the SQL query. A failed invariant of a tree that somehow made a loop…

Note: This subthread has become about exceptions in general, rather than checked-vs-unchecked. I don't mind, but I wanted to make it explicit. > Who ever handles CustomerAccessException specifically, except by calling getCause() and looking at the underlying DatabaseException? Ideally you call a method like `getDetailFoo()`, and using the inherited `getCause()` is for hacky workarounds, like when you need a change in…

> Ideally you call a method like `getDetailFoo()`

I know that's the theory, but have you ever seen it work that way in practice? Because I haven't.

> Regardless of how you write the catch-logic, the chained cause remains important for logging and diagnosis.

Sure. But if it's not being caught and handled but only logged or investigated then you're not gaining anything from the wrapper.

> Do you want layer #1 exceptions to travel all the way to #4 as-is, so that you get an HTTP 500 and you won't actually know which remote service is at fault? (Without grepping the stacktrace, which is ick.)

Yes? If it's a fail-and-alert-the-developer case then I'm going to look at the stacktrace first anyway.

I might have retries at any layer, but I don't see any case where #4 uses information from a wrapper exception from #3 to make a decision. The only thing #4 is going to do with a failure from #3 is retry or fail, and it's not going to make a different decision based on which service it was. (Maybe you want metrics or circuit breakers on the individual fortune cookie services, but in practice what I've seen is that you'd always find a way to plumb them in at layer #1 or #2, by reflection if need be, rather than have them work of the exceptions coming out of #3. I know the theory you're talking about, I've just never seen it actually work that way)

Post reply on HN