4 ms·
> You know how much code I’d have to write if this were C++, C#, or Java? None. They all have reusable notions of an immutable, ordered map. Actually C++ doesn
by humanrebar 9y ago
> You know how much code I’d have to write if this were C++, C#, or Java? None. They all have reusable notions of an immutable, ordered map.
Actually C++ doesn't have immutable data unless you count compile-time constants and literals. You can declare things 'const', but that only provides a read-only (1) view on mutable data.
Also, to provide good 'const' support in containers, you usually have to provide extra read-only iterators that are largely identical to the writeable iterators. Go look at the C++ standard library. All the containers have both 'iterator' and 'const_iterator' (not just a 'const iterator').
Another approach is to just declare member variables 'const'. So you have a 'const map<int, int>' as a member variable. Except for that to work, you need to construct that member variable in the initializer list. The body of the constructor is too late. This likely means taking an already-initialized map as a constructor argument and copying it (probably exposing implementation details in the process) or writing a helper that returns the appropriate map type and copying it. Good optimizers will elide many or all of these copies, but relying on quality of optimizers isn't portable.
Anyway, I'd say the complexity of the Go approach isn't significantly worse that the C++ alternatives, if it's worse at all.
(1) Of course, with casting shenanigans or use of the 'mutable' keyword, anyone can circumvent 'const' without a lot of work.
- user5994461 9y agoAnd java doesn't have them either. You need to use an external library, likely the google containers library.
- Sharlin 9y agoJava does have unmodifiable wrappers but they implement the same basic mutable Map/List/etc. interfaces, just throwing at runtime if mutating methods are called. Not exactly optimal but better than nothing.
- fauigerzigerk 9y agoBetter than nothing but worse than the C++ solution of returning a const reference which can be checked at compile time. Both are just immutable views on top of mutable containers. But in both C++ and Java it is trivial to create a truly immutable container of T. Not so in Go.
- kuschku 9y agoBut you can build and use generic immutable collections.
- humanrebar 9y agoThis is true. My beef with Java collections is the type-tree-gymnastics library authors have to do to get the interfaces exposed properly. Generally people end up with a family of ImmutableContainers and a family of Containers that parallel each other. The mutable Containers usually inherit from the immutable versions. This is possible, but it's an unfortunate form of code duplication.
- searealist 9y ago> Actually C++ doesn't have immutable data unless you count compile-time constants and literals. You can declare things 'const', but that only provides a read-only (1) view on mutable data. const std::set<int>& s // This is a read-only view. const std::set<int> s // This is immutable. > Another approach is to just declare member variables 'const'. So you have a 'const map<int, int>' as a member variable. Except for that to work, you need to construct that member variable in the initializer list. The body of the constructor is too late. This likely means taking an already-initialized map as a constructor argument and copying it (probably exposing implementation details in the process) or writing a helper that returns the appropriate map type and copying it. Good optimizers will elide many or all of these copies, but relying on quality of optimizers isn't portable. Foo() : s(helper()) {} // This is a move, not a copy.
- blub 9y agoYes, humanrebar is really grasping at straws. :) I would also mention that using mutable and const_cast is frowned upon and it can be trivially searched for and forbidden. Their comment about const iterators also misses the mark. Yes, the designers of the C++ standard library did a bit more work and now everyone has a high-quality type-safe dictionary implementation available. That's how it's supposed to work...
- humanrebar 9y ago> humanrebar is really grasping at straws. :) On the contrary, I've had to mentor a lot of C++ developers where they were surprised when their put-const-on-it approach to providing safety resulted in production bugs. I think it's fair for people to be surprised by this sort of thing. And I don't think the things Go developers have to do are all that much worse than equivalent code in C++. In C++, 'const' mostly means the compiler will give you nice diagnostic errors if you do mutable things to 'const' things. There aren't any guarantees about the data itself being unchanged. > the designers of the C++ standard library did a bit more work My point was that all container implementations need to do the same work if they want both const and non const iterators. You can't just return 'const iterator' from you 'begin' method because that implicitly converts to a non const 'iterator' and a mutable view of your container. > Yes, the designers of the C++ standard library did a bit more work and now everyone has a high-quality type-safe dictionary implementation available. I very much appreciate the hard, mostly unpaid, work that people put into the C++ standard. But everyone has to do a bit more work if they want, say, a read-only key-value store laid out in contiguous memory. The standard key-value stores are a pretty straightforward rb-tree and a suboptimal (to allow for iterator semantics) hash table. It's not hard to implement (again, due to hard work from the standard committee) a sorted vector<pair<Key, Value>>. It's a bit of arcane work, however, to make that optionally const or to have it share an interface with map and unordered_map.
- jpz 9y agoLet's take this const map: const std::map<int, int> constMap = { {1,1}, {2,2 } }; Or take a const map copy-constructed from from a non-const map, if you wish. How is this not immutable data, and "only provides a read-only view"? It is linguistically guaranteed to be non-mutating - const-casts excepted of course. All languages that are constructing const data types on the fly are putting them in a writeable pages in memory, after all.
- logicchains 9y ago>It is linguistically guaranteed to be non-mutating - const-casts excepted of course. Technically even after const-casting, if it was declared const then it's still not possible to mutate it, as doing so is undefined behaviour. "Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior." - http://en.cppreference.com/w/cpp/language/const_cast http://en.cppreference.com/w/cpp/language/const_cast It could for instance have been stored in non-writable memory, so any attempt to write to it will instantly segfault. This is one of my favourite things about C++ as people engaging in terrible programming practices like const-casting a const object get their just desserts, so to speak.
- Dr_Emann 9y agoYes, if you use the escape hatch from the type system, the type system won't help you. Do you also avoid the use of strong types since you can always cast to a *char?