22 ms·
> We ended up in situations where we had lots of data that looked very similar entering from common endpoints that required different business logic at multiple
by adamkl 6y ago
> We ended up in situations where we had lots of data that looked very similar entering from common endpoints that required different business logic at multiple points in the pipeline. If you’re writing idiomatic Clojure this is the toughest case possible, as you end up littering your code with extremely similar cond-trees, which makes extension and verification unnecessarily hard.
Isn't this where you would use multi-methods? If you can determine what the "type" of data is via `cond` (sounds like home-grown pattern matching?), couldn't you replace these situations with multi-methods that switch on the same conditions?
- ashtonkem 6y agoMulti-methods suffer the same drawback as repeated conds. You end up having to maintain multiple multi-methods in multiple locations that all need to cover all the same cases. If anything else, multi-methods are marginally worse than conds, because the actual implementation can be spread out and harder to visually check. If you can get away with one multi-method, that's fine. The moment you start needing multiple multi-methods in different points in your pipeline then things begin to suck. > sounds like home-grown pattern matching Yup! And if you find yourself constantly writing pattern matches in Clojure, then you probably actually want classes, since that's a much better way to bind both data and behavior together.
- raspasov 6y agoHave you considered core.match ? I’ve used it to dispatch API calls based on just data shape.
- ashtonkem 6y agoI know that this isn't your intent, but suggesting that a bunch of professional Clojure programmers just didn't think to use core.match is borderline insulting. The problem wasn't "doing the matching is hard", the problem was "doing the matching in multiple places and making sure that we cover all cases in all places is starting to look like a maintenance nightmare".
- adamkl 6y ago> "doing the matching in multiple places and making sure that we cover all cases in all places is starting to look like a maintenance nightmare" That sounds more like an indictment of functional programming rather than Clojure specifically. I’m not a seasoned functional programmer but I often read glowing praise from others about the use of pattern matching in their code. I’m also not a professional Clojure programmer, but I thought that multi-methods were supposed to be the solution to the expression problem [0] that both class-based inheritance and pattern matching suffer from. It was my understanding that multi-methods are supposed to allow open extension of behaviour without having to do what you just described; track down every instance, and cover every case. In the case of multi-methods, couldn’t you just co-locate the method implementations next to the data they are supposed to operate on? You should be able to add/modify behaviours without worrying about what the other data types are doing. Honest questions here. Just trying to learn more about the real world pros/cons of these approaches. [0] http://wiki.c2.com/?ExpressionProblem http://wiki.c2.com/?ExpressionProblem
- ballpark 6y agoA good post on the Expression Problem, and the benefits of Clojure's solution to it: https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions/ https://eli.thegreenplace.net/2016/the-expression-problem-an... Also, I agree, it's interesting to hear his experience report.
- ashtonkem 6y agoThe best summation I could possibly give is from the article you linked: "In object-oriented languages, it's easy to add new types but difficult to add new operations. Whereas in functional languages, it's easy to add new operations but difficult to add new types". Put more concretely: if your Java code base is full of one-off classes with little to no sub-classing or multiple implementations of internal interfaces, then Clojure might work for you. But if you have a lot of sub-classes and repeatedly implemented domain specific interfaces I would hesitate mightily before considering Clojure. My team's domain was such that the operations were very fixed, but the types expanded constantly. There is only a fixed set of things that the back office can possibly do with a bond or a future, but new types of instruments come into existence (or sometimes our awareness) with pretty surprising regularity. But the article you linked had a slight of hand trick with how they described multi-methods. They only give an example one multi-method. What if you need to emulate a Java interface with M methods on it? Well now you'll need M multi-methods with an implementation for each virtual "class". You quickly see how that falls apart if you need to add a type or an operation, since either way requires you to manually check and make sure that all operations are implemented for all types. Again, it works, but it's error prone. Defrecord & defprotocol work much better. We ended up trying them and they were ... okay. They work, with some footguns, most of which I can't remember anymore, but if you end up going too far down that road (like we did) you end up asking yourself why you're writing Java in Clojure instead of just writing Java in Java.