Concurrency, interactivity, mutability, choose two

n16f.net

52 points by billiob 3 days ago


roenxi - 6 hours ago

> But again, there is no free lunch: copying data is slow. Very slow. Of course you can optimize data representations, avoid copying binary blobs (they are ref-counted in Erlang) because it would be untenable. But it will still be horribly slow.

You don't have to copy the data; it is immutable. Why copy something that isn't going to change? Are we being charged for empty RAM? Different objects can share the same structure. Famously, this is what Clojure does. Theoretically it can be faster than mutating objects because you only have to write the parts that are changing (which is the same as a mutable object), you lose something to overheads (might be nearly negligible) and have enormous gains in situations where you might need a copy of an object for some reason because that is free; there isn't any reason to actually do a copy unless the data itself is going to be mutated.

socketcluster - 6 hours ago

You can substitute the word 'interactivity' with 'statefulness' and it makes the statement more intuitive IMO.

Concurrency + statefulness means you can't have mutability because then you would run the risk of a concurrent update overwriting another. But you can have concurrent read-only state, so long as concurrent writing is not permitted. This is the 'single writer principle' which is a variant of the 'single source of truth principle'. You can't have concurrent writing in this case.

If you have statefulness + mutability then you must do away with concurrency; a different way to avoid the same concurrent overwriting problem mentioned above.

If you have concurrency + mutability, then that's possible if you don't have state... You could have multiple independent, divergent copies of the state but not a single consistent state.

mrkeen - 6 hours ago

This doesn't follow at all!

  Dropping interactivity [keep Concurrency, mutability]:

  The most popular choice is to drop interactivity: since there is no one to randomly access runtime data, there are no concurrency issues. C, Rust, Go, the list of languages that go this way is long.
No-one to randomly access runtime data? Do "other threads" not count? There are no concurrency issues in C?

  Dropping mutability

  What if you could not really change data? Instead of reading the value of a variable, you could have the runtime systematically (and safely) copy the value and return it to you.
Why would you copy anything? Copying is defense against mutation. It's what you do for safety when you're working in a language with pervasive mutation, and it's opt-in and manual, meaning it works about as well as other opt-in and manual operations, like malloc and free.

Peeking over the fence at an immutable language and thinking "that runtime does too much copying, which is bad!" is like peeking over the fence at a GC language and thinking "that runtime does too many mallocs and frees, which is bad!"

my-next-account - 5 hours ago

You can have it all in the vast majority of cases. It just requires a lot of work. See: The Hotspot JVM.

strawhatdev - 2 hours ago

I really like pony's approach to this tradeoff. Reference capabilities provide such a nice semantic lever for thinking this stuff through.

Wish the language was more popular!

eqvinox - 5 hours ago

RCU needs discussing in an article like this. It arguably fudges the definitions of all 3 things a little, but you do get them all.

okkdev - 5 hours ago

That linked benchmark feels weird. The discrepancy is way too big. I'll need to take a look into the slower contestants.

ordu - 3 hours ago

I believe it is a Lisp related story, not some general truth. I mean, if I had a hashtable that needs to be mutated asynchronously, I'd probably create a single writer to it. Some entity that takes ownership of a hashtable and you can talk with hashtable only through it.

But if you are writing Lisp, and that hashtable is a global, then good luck ensuring that no one except the designated entity talks with hashtable directly. In particular, you can't force the user at REPL prompt to not do it.

The mutability lament in the same ballpark. Dynamic nature of LISP makes it hard to ensure that some code paths can't change values they deal with. Statically typed languages do it all the time: just pass a const reference into a function and you can be sure that it wouldn't change the value, or anything referenced from it. In LISP the easiest way is to make a deep copy of a value. Other ways would probably need some changes introduced into lisp-machine itself. So you are choosing between two alternatives: either copy everything, or to believe other code to behave like values are immutable. The former is slow, the latter just doesn't work generally.

Take Rust for example: if I have a mutable HashMap that needs a concurrent access, I'd hide it. It would be accessible directly in one module only, and the module would export safe API that you can't use to achieve data race. Technically I can make this HashMap to be global, but then Rust would rebels and force me to wrap my HashTable into a Mutex. If I added REPL to the mix, then REPL would need to lock Mutex as any other thread.

Lisp just an ancient language that doesn't have these guardrails, so you need to think things through and ofc you will be overwhelmed in any real case by all these things, and then you write that you can't have concurrency, interactivity and mutability at the same time. To have them, you have to limit yourself in how you write your program, and while you can use LISP to limit yourself, you still need to do the decision and invest some work into it. It doesn't happen magically all by itself. For magic you'd better try rust, though its "magic" requires months of fighting with borrow checker until it succeeds at conditioning you into right practices.

atemerev - 4 hours ago

Choose three.

Preemptive actor model (Erlang, Elixir, Gleam) for backend.

https://pouchdb.com for frontend.

gpderetta - 3 hours ago

A reminder that databases (as an example) provide all three without issues (at the cost of complexity in the implementation of course).