4 ms·
In Rust, if you have an & or Rc/Arc, you know that it's immutable, except for parts contained in Cell/RefCell/Atomic/Mutex. If you have an &mut or owned mut va
by devit 9y ago
In Rust, if you have an & or Rc/Arc, you know that it's immutable, except for parts contained in Cell/RefCell/Atomic/Mutex.
If you have an &mut or owned mut variable, then you know that you can only mutate it through that name.
If you have a non-mut owned variable, then you know that only the Cell/RefCell/Atomic/Mutex parts can be mutated, and only through that name.
If a Rust program doesn't contain the words "mut", "unsafe", "Cell", "RefCell", "Mutex" or "Atomic[...]", then everything in it is immutable.
That gives the same guarantees as Haskell's immutability where desired, but it allows to actually make use of CPU capabilities like writing to memory, and express basic operations like assigning a value to a position in an array.
Or in other words, what you really want is a system that lets you mutate something as long as you are the only one with a reference to it, and then make it immutable and give it around, while also allowing you to "get it back" exclusively either with static checks (via lifetimes) or dynamic reference count checks.
Haskell's pure immutability system, while safe unlike Java's, is just a very restricted version of such a system, that is not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory), or express imperative code even when they are safe.
- wyager 9y ago> except for parts contained in Cell/RefCell/Atomic/Mutex. This caveat is the problem. You lose a ton of properties when you have “interior mutability” (I.e. an object typed as immutable is, in fact, mutable). You probably can’t appreciate the benefit if you haven’t actually used a purely immutable language. Rust touts “fearless concurrency”, but it’s nowhere close to what you can get with Haskell’s guarantee. > not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory) Wrong on both counts. The vast majority of mutable algorithms can be expressed recursively and immutably. Consider haskell’s Vector library. It generates C-equivalent output a lot of the time despite being immutable and much higher level. The one exception I’ve run into is algorithms that inherently rely a lot on random array writes (like in-place random shuffles), and in this case Haskell has the ST monad for externally pure, referentially transparent mutability that cannot escape into the rest of your program. The fact that haskellers almost never need or want to use ST is very strong evidence that, in fact, you don’t really need mutable semantics at all once you’ve figured out what you’re doing with immutable data.
- devit 9y agoWell, you could change Rust so that the names of types with interior mutability have to start with an "@" or something like that, and then it would be obvious, and could expose a trait to denote that (there is Sync and the internal Freeze, but they don't quite do that). I think it hasn't been done because interior mutability is relatively rarely used, and being able to assert immutability in generic code is not actually that useful (and for things like hash map keys you'd need to implement Hash or other traits manually anyway, so you'll probably notice the issue). In non-generic code, you have to call functions like "borrow()", etc. to access the internally mutable data, so you know it's mutable. Regarding "fearless concurrency", the Sync trait ensures that, allowing "interior mutability" only if thread-safe (i.e. Mutex or Atomic). Obviously you can have race conditions between modifications of different Mutex/Atomics, but that's an unavoidable fundamental problem: even transactional systems will have race conditions if you use two separate transactions where you should have used one, and single-threaded event based systems also have them if you "defer" part of the computation improperly.
- Tarean 9y ago> what you really want is a system that lets you mutate something as long as you are the only one with a reference to it You mean like STRef[1]? Because that is exactly what STRef does. It is worth noting that STRef is significantly safer than similar rust constructs because they deal with reference cycles. On the other hand implicit resource freeing is more awkward and you still need a gc. This allows you to write programs that are asymptotically more efficient through mutability. Though there is a class of algorithms where lazy pure implementations gain the same benefits as impure ones using the sharing/implicit mutation that call-by-need provides. There is an experimental linear haskell branch which might be merged into ghc proper eventually. It is stricter then rust, though, and prevents things like reference cycles which is more awkward but safer in places. [1]https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-STRef.html https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-...
- jerf 9y agoIt would be better to rephrase the original point as "Some guarantees need to be embedded in the language itself, or otherwise you can't depend on them." Having the language itself guarantee immutability means that you can depend on it, because you know that no library will ever be written that expects you to pass things to it to be randomly mutated. Rust has a different set of guarantees about mutability, but again, you want them embedded in the language so that you can depend on them, which you can. Even though you can escape to "unsafe" in either language, the rest of the affordances of the language prevent library authors from going too crazy violating guarantees. That is, the important point isn't about "immutability", but about the guarantee. Within the Haskell community, the idea that something like Rust may be closer to what we're seeking is perhaps controversial, but certainly not beyond the pale. And some of that controversy would be that Rust may not go far enough with things like linear types rather than the idea that immutability is the end goal. Personally, I think immutability was a sensible thing to try out in the 1990s, but we have legitimately made progress since then and continue to make progress. (If you want to see a language where the 1990s beliefs about immutability really hurt it, check out Erlang. What Erlang ought to have had, in my 2017 opinion, is processes that have their own conventional mutable data storage, but with messages between processes incapable of carrying pointers/references. This would have accomplished their goal of total isolation and all communication being forced to go through copying, but without having the barrier to adoption raised by trying to teach people how to program in immutable languages, to what I found to be no great effect. Haskell does at least make some hay out of its immutability, in Erlang it is mostly just getting in the way. But back when it was written, the ideas were not as well teased apart as they are now. I don't mean this as criticism of the Erlang authors, because I think they did quite well, but this still would have improved the language.)
- kccqzy 9y agoYou make it sound like Haskell doesn’t have the capability to do things like “writing to memory, and express basic operations like assigning a value to a position in an array.” That’s utterly false. It’s not impossible or unusual in performance critical code to deal with mutable bytes strings and mutable arrays extensively. If you want you can just write to arbitrary locations in memory and cause segfaults in Haskell. Mutable arrays (this is used more often than you thought): http://hackage.haskell.org/package/vector-0.12.0.1/docs/Data-Vector-Generic-Mutable.html http://hackage.haskell.org/package/vector-0.12.0.1/docs/Data... Raw pointers (types, casts, and pointer arithmetic): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreign-Ptr.html http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig... Raw pointers (dereferencing them, reading and writing memory): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreign-Storable.html http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig... Manual memory management (malloc/free style): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreign-Marshal-Alloc.html http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig...
- spion 9y agoHaskell offers runST and runSTArray, which let you do mutation