Personal blog by JT Olds on Go, lambda calculus, concurrency, and hobby projects including AI-assisted software ports.
HN cites it for its critique of Go channels and a lambda calculus whiteboard post that one commenter found a great read for beginners.
sorry for the delay
i don't know that much about concurrency in general, so take this with a grain of salt
i feel like monitors are potentially an improvement over bare locks, but they tend to perform poorly in manycore settings and are still vulnerable to deadlock, perhaps more so due to their implicit nature. (i see that you point this out with respect to java, and also that it doesn't really have monitors.) fundamentally, blocking synchronization of any kind is incompatible with modularity
you misspelled brinch (rip)
there are lots of versions of actors, and the most influential one effectively didn't have any concurrency at all, so it's important to disambiguate in the text that you're talking about the one in agha's 01985 dissertation, which is also very influential. also your footnote says it's from "1986"; is that a different document by agha with the same title?
objects with their own threads (like concurrent actors or like processes in csp or erlang) are not the same thing as monitors, contrary to your assertion in the 'concurrency in oberon' section. there is a well-known duality between message passing between threads and control passing between monitors, but the mechanisms have quite different ergonomics and common failure modes
it's surprising that in a discussion that surveys original-modula, modula-2, csp, erlang, active oberon, c, c++, concurrent pascal, object pascal, superpascal, joyce, newsqueak, old squeak, alef, limbo, golang, java, and ada,…
I'm pretty sure the designers of Rust knows about CSP. Rust has Channels in the standard library after all:
https://doc.rust-lang.org/rust-by-example/std_misc/channels.html
Rust Channel have the nice additional that they don't panic, and don't have all the weird Go Channel Axioms[1] that you have to wrap your head around to get things to work correctly[2].
For what it's worth, I don't think Java Virtual Threads (a.k.a. project loom) has implemented an explicit concept of channels, although I guess BlockingQueue could work as a channel.
There is a big issue with bringing CSP to languages like Java 21 or Go, that use so-called "colorless concurrency" or "stackful coroutines". CSP losses a lot of its modeling power when you combin shared mutable state and pre-emptive (or invisible) parallelism. And this is exactly what both of these languages do.
"Colored" abstractions like async/await are more cumbersome (especially in Rust), but they let you know exactly at which points of execution your stackless coroutine may suspend, and you can handle synchronization accordingly.
Those functional concepts are cumbersome, but threads ("green", "kernel", "virtual", "goroutines" or otherwise) are not the panacea their proponents claim them to be. I had to debug way too many data races in my life to consider them perfect. Rust still has a lot to improve on its async front (no async traits, async closures or async iterators and the async runtime confusion), it can at least promise me…
> There is nothing wrong with hiding the nature of the async under the hood of a sync function as long as the abstraction is clean, which in most cases it quite easily can be.
This is the question and I would disagree. Hiding the asynchronous nature of any given function is a dangerous illusion. Languages that promote it through stackful coroutines ultimately lead to programs that are difficult to reason about and often don't perform well because developers lack any real control over concurrency and are completely at the mercy of an opaque scheduler. (And frankly, even languages like Go which get this 'right' ... don't. Eventually even people who like Go realize Go channels are bad[1].)
The answer here I think is going to be higher kinded types a la Rust. Asynchronous is a "fundamental property of time and space" -- ignore it at your own risk. But if the type system can elegantly capture the difference between present-values and values-to-come then you can realize the best of both words: code that reads like a single-thread-of-control but is actually heavily asynchronous. This is why 'await' style stackless coroutines and Futures prove to be so popular.
Though I agree more research is needed here. I was disappointed to see the Rust developers (who apparently convinced people to pay them to research this stuff) converge so quickly on stackless coroutines.
[1] https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad/
You can easily cause a C/Go program to crash/abort due to runtime type errors. If you've written C, you've miscast something at some point. Go in particular relies extremely heavily on runtime reflection. Both languages have poor type systems.
Go's race detector is useful and we've caught bugs using it, but it's nicer when the compiler prevents you from having those bugs in the first place.
>quite good concurrency primitives
That's gonna be a "yikes" from me, dog: https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad/
I'm not a Rust fanboy, I simply like languages where it's more difficult to represent invalid state (Haskell, Ocaml, Rust, etc.) than in the mainstream languages I suffer with in my job on a daily basis. Rust happens to have great tooling and is the most likely to make inroads on these issues I care about (runtime stability + code correctness via good type system).
https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad/
I'd add that these aren't just theoretical (EDIT: I forgot that his article linked to this paper).
https://songlh.github.io/paper/go-study.pdf
Go doesn't actually implement CSP correctly. That would require some changes. This provides a high-level overview.
https://stackoverflow.com/questions/32651557/golang-main-difference-from-csp-language-by-hoare
Threading is hard and gets a lot harder when you introduce pointers or mutable data. This is even harder when your type system is terrible. I suspect that Go trying to solve this would result in a language that looked a lot like Rust with a Garbage Collector.
I think different people read "Go concurrency model" to mean different things.
If we're talking about green threads (goroutines), I'm a huge fan. Having had a lot of success using Gevent in the Python world, it was great coming over to the Go world where that pattern was baked into the language.
If we're talking about channels, I'm really not a fan. Not nearly as easy to use as the equivalent Queue library over in Python, despite being baked into the language. My experience aligns with this post: https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad/ Almost every time someone's suggested a Go channel, I've found it simpler to use a mutex or waitgroup.