To have an abstraction where you really don't care about blocking or not you need promises/futures. Go's futures are bad. Real bad.
If you don't want the function to be non-blocking then you are fine with either method signature.
To have an abstraction where you really don't care about blocking or not you need promises/futures. Go's futures are bad. Real bad.
If you don't want the function to be non-blocking then you are fine with either method signature.
function isValidFoo(val) {
...return boolean
}
This function is synchronous now, but its contract needs to change if it internally calls something async (say, it's updated to check a value from a cache, which may trigger a DB lookup). The new signature has to add a callback parameter and drop the return value, or return a promise. Then its callers have to change how they invoke the function, and if they were previously synchronous their signatures have to change as well. Every synchronous function up the call stack needs to care about this internal change.To avoid a ripple effect, all functions have to be designed to be asynchronous from the beginning, but this increases program complexity significantly. It means avoiding built-in language features such as direct return values, and often means substituting async library functions for built-in looping constructs.
Other languages including Go aren't like that. You don't need to wrap a simple return value in a promise or callback just because retrieving it might someday involve I/O. Execution of the thread/goroutine simply resumes whenever the value is ready, and the function returns normally. In those languages, Node's distinction between sync and async is artificial and unnecessary.
You can literally say that about every labguafe that has concurrency abstractions.
The whole point of the argument is that golangs abstractions just push the callbacks (or blocking sync) to the application layer.
To me, idiomatic Go suggests that libraries, data structures, business logic, &c is encapsulated in blocking functions, and that concurrency is expressed in glue code.
What I was respond to was this specific sentiment: "With Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code." My argument was that that is simply not true. Client code must know the difference between blocking and non-blocking code because it affects the flow of information around the program.
The argument to me is that Go advertises itself as being easy to write that concurrent glue code & then fails to provide basic abstractions around it.
Everything you mention about things being expressed in glue code would be easier if Golang provided a modern promises library & holds just as reasonably for other modern languages that do.
The "node requires callback hell" is a red herring because so does Golang (that or blocking code), just it does it at the app layer instead of the framework layer.
What‽ It provides channels, which are a really nice abstraction for concurrency.
> Everything you mention about things being expressed in glue code would be easier if Golang provided a modern promises library & holds just as reasonably for other modern languages that do.
Have you actually written any Go? Using channels in Go is much nicer than using callbacks. Callbacks basically force one to write one's code in continuation-passing style, while callbacks let one work about sync points only where you need.
Channels are a concurrent safe queue, nothing more. They are not even particularly well implemented concurrent queues as they are highly contended.
What go provides for abstractions around those queues are range and select. Range is a nice way to make simple concurrency cases look like traditional for loops (and is very rarely used in practice as simple concurrency cases are rare, at least for me).
Select is a nice addition to the language, yet the edge cases around channels make it very difficult to reason about without keeping http://dave.cheney.net/2013/04/30/curious-channels open.
> Have you actually written any Go? Using channels in Go is much nicer than using callbacks.
I have, for 2 years, full time. I have not used node. Have you ever used a language that supports concurrency besides Go? Go does not provide, as part of its concurrency abstractions, things that are deemed bare necessities by other languages such as timeouts, cancellation, supervision, lock free structures, etc.
The entirety of the Golang concurrency story can be wrapped up with 3 things a) the culture of the language prefers message passing concurrency b) the scheduler is a very good example of when simplicity gets you great performance in the main cases c) select as a language keyword is interesting.
By leaving the basics out you push all of that work to the application level, which is why I think the callbacks argument is a red herring. At the application level you are either going to encounter callbacks (as in the stdlib http handler) or blocking code (subject to races, deadlocks, etc).
Please explain.
As for the go futures being bad, channels generally are a bad concurrent queue (contended, lack basic abstractions, etc) and using a single item queue as a future isn't in and of itself bad, but does mean you can't optimize for different usages that futures might have over message passing queues.