4 ms·
You're not solving the same problem. You're fixing the keys of the nested maps to a single type, but the difficulty is when the keys are all different types. I
by weavejester 9y ago
You're not solving the same problem. You're fixing the keys of the nested maps to a single type, but the difficulty is when the keys are all different types.
In other words, you want to type a general form of:
Map a (Map b (Map c ... z)) -> (a, b, c...) -> z -> Map a (Map b (Map c ... z))
And even that is more limited that Clojure's `assoc-in`, as the type signature still assumes each nested map is homogenous.
- tel 9y agoWhen they keys are all different types then you've got data Value = ... | VMap (Map Value Value) ... assocIn :: [Value] -> (Value -> Value) -> (Value -> Value) I was again assuming types more informative than Clojure. Also again, lenses actually solve the higher information type situation that you're pointing at in your comment.
- weavejester 9y agoYes, you can write the function using entirely dynamic types: Map Dynamic Dynamic -> [Dynamic] -> Dynamic -> Map Dynamic Dynamic But would you actually write a function like that? Of course not. Not only is it a pain to unwrap nested maps of dynamic types, but you lose almost all the advantages of a static type system. So what's the alternative? As you point out, a more idiomatic way is to use lenses, as they solve a similar problem but can be typed. However, in Clojure we can use assoc-in or lenses. Dynamically typed languages are not constrained by what is feasible to statically type. In Haskell, it is hard to the point of being infeasible to robustly type assoc-in, so it's not idiomatic to use it. But in Clojure we're not restricted by the limitations of a static type system; we can use functions like assoc-in. This is the trade off of static typing. A static type system limits what solutions are feasible and idiomatic; even functions that are simple to express and reason about, can be extremely hard to accurately type.
- tel 9y agoI wouldn’t because once I’ve got a type system the tradeoff never works out. But the same thing can be expressed. I don’t see the thrust of your argument. You can write both styles of program in both languages, but you can only have type guarantees in a language with a type system. So, you’ve shown that assoc-in can be written so long as type guarantees are thrown away. That seems like a reasonable reason to pick lenses either way.
- weavejester 9y ago"You can write both styles of program in both languages" No you can't, not in any way that's practical. It's possible to write Haskell by wrapping every value in a Dynamic type, but possible does not mean feasible. If you don't believe there's a distinction, then try writing a functionally equivalent `assoc-in` in Haskell. I don't mean sketch out a design in pseudo-code; I mean produce a fully working function that operates seamlessly with all possible types of Data.Map. "So, you’ve shown that assoc-in can be written so long as type guarantees are thrown away. That seems like a reasonable reason to pick lenses either way." Well, that's the question: is it?. Lenses can be typed, but `assoc-in` is conceptually simpler. Haskell advocates using only solutions that can be typed; Clojure advocates using the simplest solution. Static typing is a constraint, because it limits the solutions that are feasible and idiomatic. In return it provides a compile-time guarantee. The question is whether the guarantee is worth the solutions that are (for all practical purposes) removed. Immutability is another type of constraint. As is automatic memory management, variable scoping... programming languages are designed around constraints. Pretending that you can introduce a constraint and not narrow the solution space is wishful thinking.
- tel 9y agoIt’s feasible and easy to write assoc-in atop of Dynamic... it’s just not done because nobody programming in Haskell thinks it’s valuable. We could choose to support much more opt-out dynamic behavior no sweat, but nobody wants to. The idea of assoc-in working for every type of Map also doesn’t make sense. Map only exists statically. Types as Clojure discusses them exist at runtime, are statically Dynamic, and it’s trivial to write assoc-in that works over all of them in any language. Your argument hinges on the idea that it’s impractical to write using Dynamic in Haskell. I do not think this is true. There is perhaps some value in greater library support, but it is easy to do this. It is simply not considered valuable.