5 ms·
I would agree with all of these points except "pattern matching". Yes several libraries implement it, but it is not built in and even the best libraries feel cl
by macmac 6y ago
I would agree with all of these points except "pattern matching". Yes several libraries implement it, but it is not built in and even the best libraries feel clunky compared to for instance the built in destructuring. Rich explicitly rejected pattern matching ala ML for the reasons provided here: https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f#why-no-pattern-matching https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd6...
- nojito 6y agoCalling pattern matching brittle is absolutely hilarious and shows how out of touch he is. F#'s pattern matching is a clear example of it done correct.
- fiddlerwoaroof 6y agoI always found core.match really nice: but, in general, I strongly prefer using multimethods for the sort of thing other languages use pattern-based dispatch for.
- bmitc 6y agoI wish he gave more examples in that response because I'm generally confused what he's talking about. I'm familiar with Racket and F#, but not having used Clojure, I'm missing some context about the Clojure ways of doing things and examples of the problems he claims. > I feel about them the way I do about switch statements - they're brittle and inextensible. That is not the case in a language like F# or OCaml. I do note that F# was introduced only slightly before Clojure was, but pattern matching provides nicely extensible functions and are anything but brittle in those languages. Also, active patterns in F# allow one to extend the pattern matching functionality. > The binding aspect is used to rename structure components because the language throws the names away, presuming the types are enough. Again, redundantly, all over the app, with plenty of opportunity for error. I'm not sure what he means here. Again in a language like F#, names of the data aren't thrown away. They are pattern matched against, only being "thrown away" to do actual calculations. Nothing is ever lost where the data came from. For example: type Shape = | Circle r | Square s let area shape = match shape with | Circle r -> System.Math.PI * r * r | Square s -> s * s There's no confusion here. In fact, pattern matching in a language like F# allows one to completely remove the possibility of error. For example, this really shows off in parsing applications. Once your parsing function returns a type that can be pattern matched, it's extremely difficult to have an error in the pattern matching sections of code. These are typically the most robust parts of the application. > I'd much rather use (defrecord Color [r g b a]) and end up with maps with named parts than do color of real*real*real*real, > and have to rename the parts in patterns matches everywhere (was it rgba or argb?) I don't understand this either. In F#: type Color = { R: float; G: float; B: float } let colorFunction { R=r; G=g; B=b } = r * g * b No names are thrown away. Also, the comment on rather using maps seems to assume the data type for every element of the data structure is the same. How do you just use maps when the underlying types of your record aren't the same?
- pinchhit 6y agoI think Rich's point may be specific to Java switch statements and their limitations. (I know he's familiar with MLs, but I would imagine his frame of reference is mostly colored by Java's control structures.) The idiomatic clojure for your color example would leverage names in the same way: (defn color-function [{:keys [r g b]}] (* r g b))
- adamkl 6y ago> I feel about them the way I do about switch statements - they're brittle and inextensible. What is meant by this statement is that pattern matching violates the open/closed principal. If you add a new type to switch on, you need to update all the pattern matching code in the whole application to account for the new type. It’s one of the two sides of the “expression problem”[0] (the other being object oriented polymorphism). Clojure’s approach to this is to use “multi methods” which is sort of a “pattern matching”/“strategy pattern”. You are free to add in a new implementation of the multi method without having to update existing code Here is a great post that talks about Clojure’s approach to polymorphism and covers multi methods in detail: https://aphyr.com/posts/352-clojure-from-the-ground-up-polymorphism https://aphyr.com/posts/352-clojure-from-the-ground-up-polym... [0] http://wiki.c2.com/?ExpressionProblem http://wiki.c2.com/?ExpressionProblem
- sweeneyrod 6y agoI think the artificial shape example does not adequately show how variant types/pattern matching are actually used in languages like SML/F#/Haskell. It combines two different cases. Suppose you have a type `colour` defined as `RGB(int, int, int) | HSL(int, int, int)` and then you add representation as CIE. Then having to update each match on a colour is absolutely a feature not a bug. If you miss some out then your code will be wrong. On the other hand, suppose you have various ways of serialization (JSON/XML/s-expressions). In this case, it would probably be nice if you could add a way to serialize to e.g. protobufs without having to jump around your codebase and all its clients fixing type errors. But in most languages of the kind we're discussing, you can do! You just have to represent the different serialization methods in some way other than a variant type. For instance, in OCaml you could just use classes and inheritance (although in practice you probably wouldn't because the language provides nicer tools).