Live data from Hacker News

What Color Is Your Function? (2015)

journal.stuffwithstuff.com

51–60 of 90 posts

Re: What Color Is Your Function? (2015)

#51
post #30

Earlier quoted context omitted.

Except in Go you could just as easily call: foo := bar() and if bar() is a function that does something annoying or time-consuming before finishing its work and returning the value, it just behaves like a normal, synchronous function call. “Await” is, as TFA explains, syntactic sugar for slapping an async function into behaving like a normal synchronous function call. In Go there is no reason you can’t just write you…

You mean if you rewrite bar so it didn't use a channel? That's syntactically different though, isn't it? Would the Go runtime lock you to that specific thread and block it or sleep that goroutine and move it to another thread when its no longer sleeping? Maybe in Go there's no difference in those concepts but in many languages certain threads are special. UI threads are often used for event serializiation but beyond…

> Would the Go runtime lock you to that specific thread and block it or sleep that goroutine and move it to another thread when its no longer sleeping?

By default, the Go runtime does not guarantee any affinity between Go-level threads (goroutines) and OS threads. There's a way to forcibly pin a goroutine to a specific thread[1], but it limits your concurrency.

The Go approach does indeed cause problems wrt. OS-thread-affine state. It's a tradeoff. For RPC-oriented network services, the Go niche, OS thread state is very rare.

[1]: https://golang.org/pkg/runtime/#LockOSThread

Re: What Color Is Your Function? (2015)

#52

Earlier quoted context omitted.

It's like calling classes colored functions because you can both call them. Classes are not functions. They are classes. Coroutines are not functions, they are coroutines. Different primitives. A function has a single exit entry point and a single exit point and no state. If you call a function, you run its body. A coroutine has several entry points and exit points, and an internal state. If you call a coroutine, you…

> A function has a single exit entry point and a single exit point and no state. If you call a function, you run its body. > A coroutine has several entry points and exit points, and an internal state. If you call a coroutine, you make an instanciation (the body doesn't run). Sort of, in some languages, but it doesn't have to work that way. First, let's note that "blues" do have state, and they put it on the stack. S…

Yes but this require the entire runtime to be designed that way from the start.

It cannot be applied to legacy languages.

Just like you could not add a borrow checker to C without creating 2 worlds in the C community. But you can design Rust with this in mind.

A design always has a context.

Also, make a giant coroutine has a performance price, because any line is a potiential context switching.

Re: What Color Is Your Function? (2015)

#54
post #43

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…

If you really need to run your computations in a separate context/arrow, making them explicit in the types is nice. Having general syntactic rules to build, compose and run them: great.

However, if you can avoid the additional arrow entirely: even better!

I mean Haskell of all languages has green threads and doesn’t need the entire promise shenanigans.

Even in Haskell I sometimes I build very specific DSLs for my domain to be interpreted in a separate step so I can freely fix my pure and impure code.

Re: What Color Is Your Function? (2015)

#55
post #43

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…

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

I don't think it's a "good thing". I think it's a "cost". Even in nice functional languages, it's a cost, in that even with the nicest syntax there is you still get color cascades when you realizes deep in the middle of some deep function that you're in the wrong color.

There's a great case to be made that said nice functional languages, through a combination of ameliorating the costs and successfully obtaining benefits from the separation, mean that it's a good tradeoff.

Conventional imperative languages do not successfully obtain enough benefits from the specific color of "asynchronousness" (functional languages have many more colors than just that) to make it worth while. Covering it over with syntax wouldn't be enough, because it's not just a syntax problem. What you really need is to not have this particular color in the first place. (While arguably lacking other colors that you really need, but that's a story for a different day.)

Unless you refuse to pay even a little bit to avoid that color problem (which is a valid choice in some circumstances but I would submit a bad default choice for most code and most coders), we can increasingly see that there are languages that do not have the async color issue at all, and pay only modest performance prices to do so. As languages like Go and the Beam VM continue to progress, and are joined by other up-and-coming languages, even the problems with being unable to interact with C libraries fade as you can find something that does what you need for the most part. (And besides... personally I "blame" C rather than the languages trying to do better than C and then having trouble interacting with such primitive code.)

Re: What Color Is Your Function? (2015)

#56
post #21

Earlier quoted context omitted.

Agreed. Await seems much nicer for the case of some sleep/wait causing subroutine that returns something. var foo = await bar(); vs c1 := make(chan float64, 1) go bar(c1) foo := Sure you have to be in an async method to use await but its really not that bad if you embrace it. Go seems better suited for high throughput message passing.

I'm not that experienced with Go, but it seems to me that the equivalent Go code to var foo = await bar(); is foo := bar() Go concurrency is much nicer because it doesn't have colored functions. You can use the same functions and library both synchronously and concurrently in millions of goroutines.

I don't think that's true.

    foo := bar()
In Go is equivalent to

    auto foo = bar();
In C#, in terms of semantics.

It is true that, due to the runtime implementation,

    go foo()
Is significantly less wasteful than

    new Thread(() => foo());
So there is less of a need for async code in Go.

But if you want to start multie things in parallel and await all of them, or if you want to create asynchronous workflows that modify shared resources without locking, you don't have any Go library or construct to help, which was precisely what async/await gives you.

Re: What Color Is Your Function? (2015)

#57
post #21

Earlier quoted context omitted.

Agreed. Await seems much nicer for the case of some sleep/wait causing subroutine that returns something. var foo = await bar(); vs c1 := make(chan float64, 1) go bar(c1) foo := Sure you have to be in an async method to use await but its really not that bad if you embrace it. Go seems better suited for high throughput message passing.

Except in Go you could just as easily call: foo := bar() and if bar() is a function that does something annoying or time-consuming before finishing its work and returning the value, it just behaves like a normal, synchronous function call. “Await” is, as TFA explains, syntactic sugar for slapping an async function into behaving like a normal synchronous function call. In Go there is no reason you can’t just write you…

> In Go there is no reason you can’t just write your function calls synchronously in the first place.

There are exactly the same reasons in Go to want asynchronous calls as there are in C# or Java or C++, except that performance of multi-threaded code (which is the semantics of goroutines) is much nicer in Go. Sure, channels can sometimes be a nice alternative to locking, if you can afford all of the copying.

But Go is stuck with only multi-threaded code + synchronous calls + locking/channels, whereas in C# or Java or even C++ you can chose between using that OR asynchronous code with futures.

Re: What Color Is Your Function? (2015)

#58
post #26
post #21

Earlier quoted context omitted.

Agreed. Await seems much nicer for the case of some sleep/wait causing subroutine that returns something. var foo = await bar(); vs c1 := make(chan float64, 1) go bar(c1) foo := Sure you have to be in an async method to use await but its really not that bad if you embrace it. Go seems better suited for high throughput message passing.

Isn't there still a key difference there? Inserting the latter snippet in your Golang function does not require you to change anything about the function's definition or calling conventions. From the outside, it continues looking and behaving exactly as any other (synchronous) Golang function. On the other hand, if your JS function contains an await, then it must be made async and every invocation of it must be made…

> Inserting the latter snippet in your Golang function does not require you to change anything about the function's definition or calling conventions.

Neither does the C# snippet. In fact, if you want to extract the result of the function call as well, you can still keep you API as is in C#, and use Task.wait() or something similar to block execution until the task is finished, and read its result.

In Go there is no such alternative: if you have a value-returning function, and want to run it asynchronously and continue when the value is available, you have to re-write the function into a void function and add as many channel parameters as return values it held (or wrap the original function with something which calls it synchronously and then pushes those values on the channels),or use some shared memory to store the results. And make sure to also catch any panics the function might raise, if you were planning to handle panics.

I would not be surprised if some future version of Go introduces async/await to handle all of this complexity for you, except that Go developers usually hate making things easier for their users.

Edit to add: channels in Go actually tend to impose colors on functions just as much as async/await in C#. You can't call a function which returns its result in a channel synchronously, you must start it in a separate goroutine. Unfortunately, the compiler doesn't know this and will let you create a silly deadlock that way.

Re: What Color Is Your Function? (2015)

#59

Earlier quoted context omitted.

You can actually do the same thing as `go SlowThing() ` in C#, though it has more boilerplate. For example, you can do `Task.Factory.StartNew(() => SlowThing())`. The thing is, Go doesn't have async functions, because it doesn't have await. That is why you don't have colored functions in Go: it doesn't support anything as advanced. Sure, the runtime does really cool things under the hood, but the Go programming model…

Go does not have exception (exactly because of this problem.) For passing information, you use channels, which can pass more than one value back to the caller. The big thing is not running tasks in the background but the tight integration of channels and runtime scheduler that allows having an invisible event loop on top of what is synchronous programming.

I think there are many reasons why Go doesn't use exceptions, though this is possibly one of them.

Channels are nice, but they are not that hard to replicate in other languages (probably less efficiently). They have their uses, but they can't completely supplant shared memory or async/await - all 3 are nice to have in different situations.

Post reply on HN