Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

As the other commenter pointed out, this isn't restricted to strongly-typed functional languages.

Clojure has core.async, which implements "goroutines" without any special support from the language. In fact, the `go` macro[1] is a compiler in disguise: transforming code into SSA form then constructing a state machine to deal with the "yield" points of async code. [2]

core.async runs on both Clojure and ClojureScript (i.e. both JVM and JavaScript). So in some sense, ClojureScript had something like Golang's concurrency well before ES6 was published.

[1] https://github.com/clojure/core.async/blob/master/src/main/c...

[2] https://github.com/clojure/core.async/blob/master/src/main/c...




> something like Golang's concurrency

That's wildly overselling it. Closure core async was completely incapable of doing the one extremely important innovation that made goroutines powerful: blocking.


Assuming "blocking" refers to parking goroutines, then blocking is possible.

  (let [c (chan)]
    ;; creates channel that is parked forever
    (go
      (<! c)))
The Go translation is as follows.

  c := make(chan interface{})
  // creates goroutine that is parked forever
  go func() {
    <-c
  }()


No, blocking refers to calling a function that blocks. Core.async can't handle that because macros are actually not capable of handling that, you need support from the runtime.

Call a function that blocks in go, the routine will park. Do that in Clojure and the whole thing stalls.


Assuming "function that blocks" means "the carrier thread must wait for the function to return" and "the whole thing" means the carrier thread, then core.async doesn't really have this issue as long as e.g. a virtual thread executor is used.

There is a caveat where Java code using `synchronized` will pin a carrier thread, but this has been addressed in recent versions of Java.[1]

[1] https://openjdk.org/jeps/491


The post I was replying to included explicit mention of ClojureScript, where this does not exist. As it did not for Java for most of core.async's existence. And of course, for virtual threads, that's very much "special support from the language"!


> where this does not exist

Because JavaScript runs an event loop on a single thread. It's akin to using GOMAXPROCS=1 or -Dclojure.core.async.pool-size=1 . There may be semantic differences depending on the JavaScript engine, but in the case of web browsers, the only function that could possibly stall the event loop is window.alert. As for Node.js, one would have to intentionally use synchronous methods (e.g. readFileSync) instead of the default methods, which use non-blocking I/O.

When using core.async in ClojureScript, one could use the <p! macro to "await" a promise. So there is no use for Node.js synchronous methods when using core.async (or using standard JavaScript with async/await).

I would call this "making use of the platform" rather than "special support from the language". The core.async library does not patch or extend the base language, and the base language does not patch or extend the platform it runs on.


Can you elaborate? As far as I'm aware if you pull from an empty nchannel it wikl be blocking ubtik it gets a value.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: