5 ms·
I find that when working with maps and sequences of maps with higher-order functions, types are especially critical to ensure that e.g. you didn't get your argu
by quatrevingts 15y ago
I find that when working with maps and sequences of maps with higher-order functions, types are especially critical to ensure that e.g. you didn't get your arguments backwards in your fold, or forget that your map values are sequences, not single elements. So much can be accomplished with a chain of higher-order invocations, and so much can go wrong (especially if you have Perl-style type coercions), but types are great at catching the errors.
At the same time, I think that programing with complex generic structures is a functional anti-pattern anyway: if your type is "Map (Foo, Bar) (Map Int (Set String))", and it's not somehow encapsulated (as in a class or ADT), good luck. In Python you would hide this in a class, but what is the Clojure solution (genuine question)?
- Tuna-Fish 15y agoThe Clojure solution is a map of maps. There are facilities for creating types (notably, defrecord), but it's just a way to optimize your code -- the interface it offers is exactly the same as a map, to the point that if you call assoc on it (as in, add a key-value pair) you get a normal map back. This really doesn't appear to be as much of a problem as it was for me in Python and Haskell. Perhaps it's because the accessor syntax "feels right" so that manipulating even complex structures of maps and vectors seems natural, and the code won't look too convoluted. (In Clojure, maps are functions of their keys, vectors are functions of their indexes. So: (def a-map {:a 1 :b 2}) (a-map :b) => 2 also, when using keywords as keys, the reverse also works: (:b a-map) => 2 This basically makes normal operations on maps look much like using accessors on Haskell ADTs.)
- quatrevingts 15y agoThat doesn't sound like encapsulation though. If I have some complex map of maps with invariants I need to maintain, is there any way to ensure that client code doesn't mess with it?
- Tuna-Fish 15y agoThe Clojure ethos is quite anti-encapsulation. Generally, you maintain complex invariants by defining the functions that manage your data and not updating it except through them. (This is easier than it sounds, because when all data structures are immutable, drive-by-mutation just doesn't happen.)