30 comments

[ 1.9 ms ] story [ 87.8 ms ] thread
So it doesn't fit his use case. There is no silver bullet. Everything is a moneky's paw in software engineering. I look forward to the day when someone discovers a language trade-off and doesn't dramatize the entire experience.
Goodbye Discovering A Language Trade-Off and Not Dramatizing the Entire Experience, and good luck.
I like reading a bit of prose about when a tool doesn't fit someone's use case. Nim has a pretty interesting approach to data sharing in concurrency, and it's more interesting to read a story of someone who got bit by it than to read a table of language features and wonder when it might prove to be a problem.
But that's not how it was phrased at all. It was more like "I liked this thing, then I didn't like it because it didn't scale in a certain aspect as I wanted to so peace". I think studies of use cases are valuable but this was not one.
I'm not sure this blog post could have been any less dramatic. He could have not written it I suppose, but he brings up valid points that are probably useful for everyone who might be using or interested in Nim. I don't think the attitude here should be "if you don't have anything good to say don't say anything at all."
This might be a tradeoff - or it might simply be a flaw. Compared to a language that provides easy composition of managed state changes (Haskell or Clojure), or an alternate model for handling these kind of updates (Erlang or Scala), what is the compensating advantage that Nim gets for this tradeoff?
Extreme portability.
Why is extreme portability opposed to multithreading?
Because each platform has its own wildly different threading semantics? (I don't actually know if this is necessarily true.)
The problem is, there's already plenty of garbage collected mostly portable languages with slightly painful FFI.
Might I suggest looking at Rust? It started out with the policy that

"This means that a thread cannot touch the memory of another thread."

but has grown a host of smart pointer-based thingys that do exactly that.

As far as I'm aware, Rust is the only recent systems programming language that is memory-safe in parallel mode (i.e. number of kernel threads > 1). Java and C# were, by virtue of spending a lot of impressive work on their garbage collectors, but the recent languages have unfortunately tended to throw memory safety away in various cases.

(Edit: I realized I forgot Swift -- mea culpa.)

Not that I don't believe, but can you provide a citation for that fact about Rust in parallel mode? I don't follow Rust too closely, but I hadn't heard of that. That is really interesting.
(comment deleted)
Interesting read would be here: http://doc.rust-lang.org/std/thread/

It is also guaranteed by ownership system: in the safe mode (which is default) rust will statically (at compile time) ensures that a value can only be mutated at a single execution point. It is done by moving, instead of copying any value _by default_, unless it is wrapped in some kind of synchronization primitive or implements a _Copy_ trait.

Synchronization primitives themselves are implemented in unsafe mode, thus limiting the unsafe code scope and allowing custom unsafe code which can also be wrapped in safe interface.

Isn't it also the only systems programming language that is memory-safe in single-threaded mode? Possibly ATS could be another, but I'm not sure how complete and useful it is.
Well, it depends on your definition of "systems programming language", but Go for example is memory-safe if GOMAXPROCS = 1.
> ... Java and C# were...

Do you mean those langauges are memory safe only in their GC implementation, which uses multiple kernel threads? How does this affect user code? Java and C# still allow you to access memory from other threads without locking.

I'm using the Saraswat definition of memory safety (though he called it "type safety"), which admits data races but not things like use-after-free. You're right that Rust has a stronger guarantee of data race freedom. But most recent systems programming languages don't even guarantee memory safety in parallel mode.
The freedoms that allow data races are the very same ones allowing implementing concurrent data structures. To the best of my knowledge, there are no static proof systems (other than model checkers, which aren't really static) that guarantee correctness of concurrent DSs yet allow implementing all (or most) correct ones.

You can either sacrifice your guarantees or your use of efficient data structures. Of course, you can always wrap the implementation in some "unsafe" designation, and then declare the whole black box to be correct.

Of course, efficient, general-purpose concurrent data structures usually require a GC.

This is true, but also a false dilemma. Rust provides an escape hatch in the form of the `unsafe` keyword. This allows the implementation of efficient data structures, while still providing static safety guarantees to the vast majority of code.
Well, GC runtimes also ensure memory safety of those data structures (you can't really implement them too efficiently without a GC anyway) without being unsafe, while Rust provides neither memory nor race safety in these interesting cases.

Still, I think Rust design decisions are correct if it wants to fill the C/C++ niche (which I hope it will). That niche is not necessarily meant to provide the best performance (I believe JITs will soon surpass any AOT compiler, and GCs are pretty much a must to support concurrent workloads on many-core servers with huge RAM) but safety and speed in constrained environments or applications where GC/JITs are inappropriate (small RAM/short startup).

But even so, there is a need for good concurrent data stuctures. What I'm wondering is whether Rust provides a way to use the type system alongside concurrent data structures (ARCs don't cut it). Is there a way to say "this pointer is managed by a transactional data structure and can be shared freely among threads"?

Just out of curiosity, is it stated anywhere that Swift is memory-safe in parallel mode? Or is this a necessary prerequisite for reference-counted objects to be safely used with Grand Central Dispatch?

(I assume that whatever implementation exists is similar for Rc'ed objects in Rust, assuming Swift is indeed parallel-memory-safe.)

> Just out of curiosity, is it stated anywhere that Swift is memory-safe in parallel mode?

I admit to not being intimately familiar with Swift, but I assume it is (or is aiming to be), because Objective-C objects are all atomically reference counted, and the language makes an effort to section off the unsafe parts (via names like "UnsafeMutablePointer").

> (I assume that whatever implementation exists is similar for Rc'ed objects in Rust, assuming Swift is indeed parallel-memory-safe.)

Rc is not thread safe, but the language and libraries prevent Rc'd objects from ever moving between threads, so the lack of thread safety of Rc doesn't make the language less thread-safe. There is a separate type, Arc, that is thread-safe and can be sent between threads. (Arc also prevents mutation of the protected data without a mutex or atomic operations.) Swift's approach, by contrast (as far as I know) is to atomically reference count all objects. (This approach is inherited from Objective-C, and more broadly Core Foundation. It mirrors COM and various other component systems.)

Thanks! That makes a lot of sense.
I haven't built Rust recently ( :-( ), but doesn't it also have support for atomic operations in the standard library?
I really like Rust so far. There is lot of churn recently in the languages and library, but I think it is for a good reason.
Shame to see gradha go - I read and enjoyed his ranty posts on macros, and he's contributed to a lot of repos.

He is right that there was some breakage in libraries with 0.96 to 0.10.2 - I'm hoping that things will start to settle once v1.0 is released and Nim will gain more traction.