
What Color Is Your Function? (2015) - chrfrasco
http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function//
======
devxpy
I've been thinking why exactly async-await was chosen at the function level,
and not the caller level.

I mean for languages that have event loops at their core, why isn't every
function `async` by default? Let the caller decide how it wants to use the
function.

`async` functions don't wait for a result of a `Future` unless `await` is
used.

Instead of putting all that effort into putting `await` everywhere, why not
invert that logic and make them `await` by default, and instead have the
async-await syntax used by callers?

Like if one to were to write a transpiler to do this in dart, it might compile
the following -

    
    
        void main() {
            Future<int> xFuture = async doSomething()
            int x = doSomething()
             
            assert(await xFuture == x);
        }
    
        int doSomething {
            ...
        }
    

into -

    
    
        Future<void> main() async {
            Future<int> xFuture = doSomething()
            int x = await doSomething()
    
            assert(await xFuture == x);
        }
    
        Future<int> doSomething async {
            ...
        }
    
    

This might be stupid, but my solution to the "what color is your function
problem" is to make every function async.

I looks an awful lot like what go does with its `go` syntax, but because you
have an event loop with Future and Async Streams, you don't need to worry
about dealing with CSP.

~~~
chaorace
I suspect this is for two reasons:

* Many popular languages today predate modern asynchronous computing. This makes async-as-default impossible because it's an afterthought

* Async computing is harder to learn and confusing for those just picking up the language. There are some pretty major ergonomics issues that you have to solve if you want anything more than a DSL to achieve noteworthy adoption levels.

~~~
devxpy
> Many popular languages today predate modern asynchronous computing. This
> makes async-as-default impossible because it's an afterthought

Here's a rough sketch of how this can be achieved for a language that already
has event loops -

Transpile the following -

    
    
        void main() {
            Future<int> xFuture = async doSomething()
            int x = doSomething()
             
            assert(await xFuture == x);
        }
    

Into -

    
    
        Future<void> main() async {
            Future<int> xFuture = (() async => await doSomething())();
            int x = await doSomething();
          
            assert(await xFuture == x);
        }
    

Now people can keep using a pre-existing `doSomething()` (that's either async
or sync) and its compatible with our new caller based async statement.

BTW that transpiler output is runnable dart code actually works.

I know this has tons of edge cases that this simple example doesn't capture,
but it's definitely "possible".

------
franciscop
I do Javascript and wish throwing quick scripts was a bit easier, which are
made a bit harder with promises/async since now you have to separate your
scripts and functions depending on their color. So I made a library to help
me[1]:

    
    
        const name = await swear(fetch('/some.json')).json().user.name;
        console.log(name);  // Francisco
    
        const error = await swear(readFile('./error.log')).split('\n').pop();
        console.log(error);  // *latest error log message*
    

It makes all functions to look like blue functions, but internally they are
all red. I made this by using `Proxy()`[2], then queuing the operations and
waiting for any unfinished one on the last operation, which is always a
`.then()` (since there's an `await`). It is fully compatible with native
promises.

While I do not use it directly since adding a library to make syntax slightly
shorter defeats the point, I've included it into some of my async libraries:

• File handler `files`:
[https://www.npmjs.com/package/files](https://www.npmjs.com/package/files)

• Simple command runner `atocha`:
[https://www.npmjs.com/package/atocha](https://www.npmjs.com/package/atocha)

• Enhanced fetch() `fch`:
[https://www.npmjs.com/package/fch](https://www.npmjs.com/package/fch)

[1] [https://www.npmjs.com/package/swear](https://www.npmjs.com/package/swear)

[2] [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

------
benhoyt
I love this about Go. All functions are simple and synchronous, but if you
want to call them concurrently, just "go SlowThing()" and coordinate with a
channel. Compare that to the async stuff in C#, Python, etc -- it's bolted on
later, and you see double of everything.

~~~
bcrosby95
You're just using the channel as a future.

None of this is really problematic at the top level view of things. But when
you need to compose libraries or applications that make use of these things -
yes, even channels in Go - you can start running into problems.

Especially if you don't actually control the process you're running in. This
is why promises and async/await really exist. E.g. if you have code you need
to fork off the main UI thread so you don't block it, but need to re-capture
the UI thread so you can call UI update code when you're done with your long
running task. async/await is _so_ much cleaner here.

Async/await allow for much more complex control of flow than simple goroutines
and channels. Sometimes it's necessary. Sometimes it isn't. It was necessary
in nodejs. It's _extremely_ useful in applications where you have a "main
thread" that drives your application that you don't want to unnecessarily
block. Go doesn't solve this problem out-the-box. There's a reason why c#
adopted async/await despite having futures and all sorts of other tools.

~~~
philosopher1234
I don’t see how futures can be more flexible than go routines. Could you
explain that more? And why couldn’t you just spawn a goroutine to avoid
blocking your main thread?

~~~
jayd16
Sometimes you want to block the main thread, or at least finish what you were
doing. With async/await and cooperative concurrency you can be explicit about
what will run on a thread. You retain control of the thread until you yield or
await. You can ask tasks to complete on other threads or post back to the main
thread. You have a lot of control. Its easy to write code without locks that
runs concurrently on the main thread but is still able to build a UI in a
threaded way.

I don't really know how go UI frameworks work. How do you have multiple,
preemptively scheduled goroutines on the UI thread but without critical
sections? You have to use channels and message passing back to a main thread
manager to handle this, yes?

I think Java's Loom would have the same issue but again, I don't really know.
Perhaps worrying about a UI thread is 'fighting the last war' and we should
work on new UI paradigms in these new language features.

~~~
anonymoushn
You can spawn a goroutine that immediately waits on a channel, so that it will
not actually do anything until you want it to. This seems at least as
expressive as a single-threaded switch() primitive. I think it can also
express the threaded async pattern you want, but I'm not sure.

~~~
jayd16
Are there any popular, idiomatic Go UI frameworks I could explore?

~~~
oefrha
You can try your luck with [https://github.com/avelino/awesome-
go#gui](https://github.com/avelino/awesome-go#gui)

I explored the landscape earlier this year when I was building a GUI version
of a CLI tool written in Go. I was throughly disappointed by all the “native
Go” and non-webview options — narrow selection of widgets with basic
functionality missing, UIs ranging from lack of polish to horrendous looking
(that makes Java GUIs look like godsend), lots of bugs, etc. I ended up using
the Qt binding which, despite its own share of problems, at least worked
fairly reliably and didn’t constantly get in my way in every way imaginable:
[https://github.com/therecipe/qt](https://github.com/therecipe/qt)

------
bvrmn
There is an interesting approach in Zig to explicitly kick call frames to
achieve colorless asyncness.

[https://www.youtube.com/watch?v=zeLToGnjIUM](https://www.youtube.com/watch?v=zeLToGnjIUM)

------
dang
See also...

2018:
[https://news.ycombinator.com/item?id=16732948](https://news.ycombinator.com/item?id=16732948)

discussed at the time:
[https://news.ycombinator.com/item?id=8984648](https://news.ycombinator.com/item?id=8984648)

a bit more from the same day:
[https://news.ycombinator.com/item?id=8982494](https://news.ycombinator.com/item?id=8982494)

------
smabie
I thought he was talking about IO functions in Haskell. But I guess async
works too.

~~~
afc
Yeah, that was where I also had guessed he was going as I was reading. :-)

------
knrz
Also one of my favourite points about Elixir/Erlang — having no distinctions
between async/await makes programming flow better.

~~~
BiteCode_dev
That's the benefit of integrating async in the runtime itself. It abstracts it
from the language, which doesn't have to know about it. Just like a GC
abstract memory handling.

~~~
geocar
Right. Even POSIX.1 has the same "messaging" functionality, so:

* spawn/1 becomes fork()

* send/2 becomes kill()

* receive F -> ... G -> ... end becomes sigaddset(&s,F);sigaddset(&s,G);sigwait(&s,&r)

There are of course other issues, like there aren't that many signals, or that
memory management is hard, or whatever, but these are solved by _other_
languages as well, so you can imagine this is at least straightforward to
implement.

The downside is that it means you can have functions that you can't write.
That can be a big bummer to a lisp programmer, but I don't think python,
javascript, etc, programmers care about that, so I think history will judge
the trend to colour functions as lazy and shortsighted.

------
Garlef
I think this is just a matter of perspective and actually having different
colors is a good thing:

Thinking in terms of functional programming / category theory, this coloring
seems to boil down to working with different arrows. The coloring in this case
would correspont to signifying the target category of the arrows `arr/lift`
function (For example async functions in JS should be morphisms in smth like
the Kleisli category of the `Promise` functor).

The issue now seems to be that there are no nice native language constructs in
most languages to work with arrows. What's missing are arrow comprehensions -
the bridge between the compositional notation used in functional languages and
the more traditional way of writing things like `const x = ...`. The vanilla
functions without `async/await` are arrow comprehensions for the identity
monad.

The reason why the `async/await` syntax works so well as compared to using
`.then` is that it is basically arrow comprehension for the promise monad.

~~~
rocqua
I have some vague knowledge of category theory, but not enough to follow your
argument here. Especially the concept of an 'Arrow' could you explain more
clearly what an arrow, and an arrow comprehension is?

~~~
Garlef
[https://en.wikipedia.org/wiki/Arrow_(computer_science)](https://en.wikipedia.org/wiki/Arrow_\(computer_science\))

[https://en.wikipedia.org/wiki/List_comprehension](https://en.wikipedia.org/wiki/List_comprehension)

[https://www.haskell.org/arrows/syntax.html](https://www.haskell.org/arrows/syntax.html)

[https://www.sciencedirect.com/science/article/pii/S157106611...](https://www.sciencedirect.com/science/article/pii/S157106611100051X)

------
andrewzah
This is something I ran into recently when learning about promises and async
functions in javsscript. It took me a while to grok it, and I'm still not sure
if I fully understand it, but we settled on using try/catch blocks with
typescript's await keyword on calls we need to block on.

I wanted to use @usefultools/monads to have Maybe types, etc, but it's easier
to just stick with promises throughout the chain. Essentially working with
functions that return promises (like database calls with Mongoose) makes you
convert your functions to also use promises. As far as I can tell there's no
way to unwrap a promise (similar to Rust) without the await keyword inside a
try block.

I don't know if this is exactly the best approach, but it's significantly less
verbose than .then().catch() callbacks. I also learned about Promise.all() and
Promise.allSettled() recently here [0].

I wish I could stick to futures with Rust or goroutines with golang. Bleh.

[0]:
[https://news.ycombinator.com/item?id=23223881](https://news.ycombinator.com/item?id=23223881)

------
gorgoiler
Unrelated but by complete coincidence I came across the author’s blog
yesterday, while researching book creation in AsciiDoctor and Markdown.

His write up on the process is also interesting:

[http://journal.stuffwithstuff.com/2014/11/03/bringing-my-
web...](http://journal.stuffwithstuff.com/2014/11/03/bringing-my-web-book-to-
print-and-ebook/)

------
KingOfCoders
Still we need a language where every function call is async and the runtime
decides what to inline.

~~~
hombre_fatal
Looking at my async code, I just don't see how that would make things more
clear instead of less clear.

For example, something as simple as:

    
    
        const result1 = promise() // start this promise first but don't await it yet.
        // ...
        const results = await Promise.map([promise(), promise(), result1])

~~~
KingOfCoders
It's more that you don't need to add 'async' to each function, because every
function is async. And working with promises automatically maps over them.
Then if you want to 'unbox' the Promise you'd have a keyword, like await.

So instead of a.map(v => f(v)) ... or Promise.map(a, f) ... or a.map(f) ...
it's just f(a).

If you then want the value, in the end, if at all (e.g. the web framework
understands Promise and you return a Promise in your controller and not a
value, you don't need await at all).

You'd need some syntax for Promise.all(...) though to "join" concurrent
executions again.

The language should probably have some syntax for multiple writers, e.g. and
atomic compareAndSet.

    
    
       shared v = { .... }
       v.change(v, f) 
    

where f has no side effects and can be executed again. With IO (DB) one would
need support for idempotency ala Stripe.

But your functions would not have colors.

------
joshribakoff
Classic article. In rxjs mapping over a sync array or async stream is exactly
the same, for me the problem in the article is not such a big problem in
practice for day to day work, especially with tools like rxjs which makes
composing mixed code easy

------
elwell
Original discussion:
[https://news.ycombinator.com/item?id=8984648](https://news.ycombinator.com/item?id=8984648)

------
k__
I saw TypeScript has now an _awaited_ keyword that will eat promise wrapped
and plain values.

------
afc
Interesting read.

I'm not sure I agree with the conclusion: I find using futures with very
carefully controlled threading a preferable paradigm (for how I structure my
programs in C++, at least for my text editor:
[https://github.com/alefore/edge](https://github.com/alefore/edge)).

In my experience, using sync code looks more readable on the surface but just
kicks the can down the road: you'll still need to deal with the complexity of
threading, and, in my experience, it's going to be waaay uglier. What you gain
with your "superficial" simplicity, you pay a hundred times over with the
algorithmic complexity of having to use threads with shared state. Every time
you're troubleshooting some weird race condition that you can't easily
reproduce you'll be wishing you had just used futures.

What I do is that the bulk of my processing runs in the main thread and I
occasionally dispatch work to other threads, making sure that no mutable state
is shared. When the "async" work is finished, I just have the background
threads communicate the results to the main thread by setting a future (i.e.,
scheduling in the main thread the execution of the future's consumer on the
results). Async IO operations are modeled just the same. In the beginning I
had used callbacks spaghetti (mostly writing things in continuation passing
style), but I started trying futures and found them _much_ nicer.

I'll admit that on the surface this makes my code look slightly uglier than if
it was directly sync code; however, it allows me to very safely use multiple
threads. I think not having to make my classes thread safe (I typically stop
at making them thread-compatible) and not having to troubleshoot difficult
race conditions (and not having to block on IO or being able to easily run
background operations) has been a huge win. If I had to rewrite this from
scratch, I'd likely choose this model again. My editor only needs to use
mutexes and such in very very few places; it suffices to make my classes
thread-compatible and to ensure that all work in threads other than the main
thread happens on const objects (and that such objects don't get deallocated
before the background threads are done using them).

I rolled out my own implementation of futures here:
[https://github.com/alefore/edge/blob/master/src/futures/futu...](https://github.com/alefore/edge/blob/master/src/futures/futures.h)
(One notable characteristic is that it only allows a single listener, which I
found worked well with "move" semantics for supporting non-copyable types that
the listener gets ownership of.)

Here is one example of where it is used:
[https://github.com/alefore/edge/blob/5cb6f67e1e0726f8fbe12db...](https://github.com/alefore/edge/blob/5cb6f67e1e0726f8fbe12db3d1e0cd12b07d5135/src/buffer.cc#L919)

In this example, it implements the operation of reloading a buffer, which is
somewhat complex in that it requires several potentially long running or async
operations (such as the evaluation of several "buffer-reload.cc" programs,
opening the file, opening a log for the file, notifying listeners for reload
operations...). It may seem uglier than if it was all in sync code, but then I
would have to either block the main thread (unacceptable in this context) or
make all my classes thread safe (which I think would be significantly more
complexity).

I think this is cleaner than callbacks spaghetti because the code still
reflects somewhat closely its logical structure. For loops I do have to use
futures::ForEach functions, as the example shows, which is unfortunate but
acceptable. In my experience, with callbacks spaghetti, it is very difficult
to make code reflect its logical structure.

------
sergiotapia
Thank god i code in elixir

~~~
waynesonfire
I have similar sentiment when I'm reviewing modern java code that's riddled
with Futures.

------
longtermd
I love the idea of coloring functions based on how "heavy" they are on the CPU
or how long they take to execute on an "average client desktop/mobile
browser".

~~~
saagarjha
I think you’re imagining a flamegraph.

------
z3t4
This article made me angry then I first read it, and now it makes my blood
boil. First off there where no colors in JavaScript pre ES6, there where only
functions. You can call them first class functions, but those are still
functions. A callback is just a function! Then we have side effects, but
executing the side effect while returning a promise like in ES6 you are still
executing the side effect, even if yo do not call .then() so nothing really
changes.

So first you think there is a problem, but there are not any, but by solving
the imagined problem you do actually create it. You now have generator
functions and "async" (with the async keyword) functions that always returns a
promise, _and_ normal functions.

~~~
saagarjha
I think you didn’t really understand the argument. The author is not saying
that the language lacks first-class functions, in fact he requires it for his
real point, which is that async and sync functions are not really compatible.

~~~
z3t4
Prior to ES6 JavaScript did not have async functions, just normal functions.
Async functions came from the runtime, not JavaScript the language. For
example there is no setTimeout function in JavaScript, it has to be
implemented by the framework that runs the JavaScript. Talking about an
function being async makes it confusing though. It's better to think of them
as events. You call a function that executes a job, and specify a function to
be called (an event handler) when the job has been completed. Putting the
callback function in the function arguments is just a convention, other
conventions are foo.on("ready") = handlerFunction; foo.ready =
handlerFunction; foo.error = errorHandler; bar(handlerFunction);
biz(successHandler, errorHandler); The callback convention is less verbose and
probably why it's the most popular. Using just functions makes you more
powerful...

