Hacker News new | ask | show | jobs
by gpderetta 23 days ago
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 problem.

Some statically typed languages (Go) do not encode async-ness statically, so it is not an issue[2].

Some dynamically typed languages (scheme, Lua, lisp) also do not encode async-ness statically. Everything is fine.

Finally there are some dynamically typed languages (python, js) that, eskew static types but for some reason still decide to encode async-ness statically. For me this is the most bizarre decision, especially as some of the justifications for static async-ness (performance, memory usage) are less relevant.

[1] for example, a litmus test is being able to implement an higher order function that inherits its color from one of its parameters. Essentially this is the problem of generically turning an internal iterator to an external one.

[2] fundamentally in these languages continuations are first class values that can be passed around, so the asyncness is naturally not bound to the function that created a continuation.

Edit: you can in principle abstract away color in any async language by simply assuming that any function call is async and await it. Then sync functions can trivially be made async. But at this point async annotations no longer convey any useful property: the language might as well implicitly await any function and require call-site annotations for diverging control flow or reentrancy requirements. More practically as languages with async evolve and grow asyncness becomes pervasive and pushes away any sync component.

2 comments

> 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 `<T>(f: () => T) => T` because an async function statically is just the return type wrapped in a Promise, not something like `async () => T` you can still pass in an async function as an argument.

I think that's a reasonable thing to take issue with, and its _possibly_ an avoidable design problem. That said I can see it being less avoidable if the async function requires some special kind of invocation (like being associated with some kind of async runtime and its a compiled language).

When I see people bring the issue of function colouring, the focus tends to be on the fact that a function is no longer interchangeable with a sync function and now you have to handle a promise, which I personally find unconvincing if the return type really should be a promise then it shouldn't be interchangeable with a sync function.

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<E>, ...). Such a function should only really return a promise if it's given a vector of promises, and, with an interface that "thing" supports, can be written generically for all those things.

(In Rust, there are separate implementations of that for Option and for Future.)

Higher-kinded types are the (a?) design solution, but they _do_ come at a cost, and for some that cost is higher than the cost of colours.

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

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

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

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