Clojure Design Patterns
mishadoff.com
mishadoff.com
People have been saying that:
- Global Object is bad
- Stateless is better than Stateful
- Singleton becomes an anti-pattern
- Methods/Functions without side-effect (avoid mutation)
Nobody really mentioned to do things the Functional programming way per-se but the characteristics are there.
You can throw "const" on things but that isn't really the same kind of immutable programming that exists in Clojure.
Because immutable programming is not just about data not changing, it's about the transformations you can do on that data without concern for the costs that would arise if you did those same transformations in a language without immutable data structures.
"Modern Javascript ES2020" + lodash + immutablejs
But lodash and immutablejs don't mix ;) and still, with either two, it won't be as consistent.
But nodejs and its ecosystem is just to hard to ignore for web apps, it really has too much good stuff (and bad too).
That said, at the new place that uses Rails we instilled a culture of avoiding stateful updates, preferring hash structures to mountains of object definitions, and use of functional transforms as much as possible.
Then we needed to ramp up to the next level and chose the JVM for writing concurrent code. But we started with Scala as something that has functional concepts baked in since it's "easier to hire for". Not the same, but there are other paths to dealing with lack of Clojure.
Oh, and the Scala only makes up for part of losing Clojure because we actually use it in a similar way to Clojure plus a bajillion type declarations. With the occasional workaround for a compiler bug (I've encountered three in the past 6 months).
This seems like a bad idea from a business value perspective. What value is delivered by translating mountains of code? Probably none, being honest.
As I am also partially Java guy, I came to conclusion that migration of project into JRuby and then embedding Clojure parts gradually might ease the transition.
Don't transition everything, giant waste of time. Run ruby/rails on jruby, then package the core Clojure logic up as a library, and call into it from rails where needed. Clojure is your core logic, ruby/rails is the web front end to that core.
We're growing to the point of needing a few distinct components after building a Rails monolith that already had very strong internal boundaries. As we split out different parts to run on different machines, they will either get an immediate re-write, or eventual re-write from behind the service interface.
“If functional programming is so great, why is it still niche? We have a product that can practically eliminate runtime errors, make refactoring much easier, lighten the testing burden, all while being quite delightful to use. What’s the hold up?”
- http://www.elmbark.com/2016/03/16/mainstream-elm-user-focuse...
I'm just getting into Clojure and enjoy working with it so much that I don't care if there are job opportunities at the end of it to justify the time investment and mind-reboot. It's the first language that I didn't feel like I was fighting, and Parinfer has made all those “weird parentheses“ a joy: https://shaunlebron.github.io/parinfer/
That said, there do seem to be more opportunities than last time I looked at Clojure/Scala/Haskell (Functional Works, mentions in HN Who's Hiring) – it feels like people are waking up to FP ideas after React stormed the front-end scene.
I think the issue for me is I predominantly build web apps. Can you recommend any resources for building modern web apps in Haskell or at least fitting it into my day to day at work?
Haskell-like (but taking away some of the pain points), for the frontend, compiles to very readable javascript.
There's an effort to make something better and on the same vibe as Haskell's `stack`, which is `psc-package` [1].
I switched to it for all new projects, and while it still has some rough edges, I don't really miss bower.
I think they are over-estimating how much people care about runtime errors.
JVM doesn’t apply hence.
Error handling is somewhat over-emphasized in comparison to other things that affect productivity (and maintainability).
It's niche because learning a language is the exact opposite of fast iterative process with immediate feedback. The feedback cycle of learning and evaluating a language is 6th months to multiple years, which means it takes a lot to really evaluate one. Then the added issue of network effects (ie needing others to know the language in a team).
Imperative and mutable objects are still seen as the only way for serious products .. there needs to be a large body of evidence to at least make people consider other options (and I'm not trying to sell my church here, just avoid blindness)
And I'd bet money that if that same project were translated into ClojureScript, it would be less than one-third that size for the same benefits.
Now, as a side note, there were recent news about clojure CLR. Maybe some will accept having clojure code on top of C# ? (clojure is often used as a better lifestyle on top of Java legacy)
This is technically an accurate statement, the JavaScript ecosystem is massive and has excellent equivalents to just about every piece of useful Clojure FP construct out there.
But from a practical standpoint, when you start stitching these FP pieces together, you'll notice that the FP model in JS suffers immensely from the mutable-by-default design in its core data structures.
This means FP libraries have to accept and output these mutable-by-default data structures to gain any non-trivial adoption, which in turn often means they can't afford to use persistent data structures internally either, because recursively translating to and from the default mutable data structures, even just on the edges of the system, will often prove to be prohibitively expensive for most libraries. The entire JS ecosystem thus overwhelmingly assumes the use of mutable data structures, and in a program that tries to do FP in JS, any performance wins resulting from the use of structural sharing in persistent data structures (instead of naively cloning the default mutable data structures) get mostly nullified, if not outright negated by the need for recursive translation every time it needs to interact with third-party libraries or core browser APIs.
Even in systems that uses Redux (which as an architecture is about as functional as you can get, and outright forbids direct state mutation) overwhelmingly end up using built-in mutable data structures to represent state. Its Recipe page on "Using Immutable.JS with Redux" (https://redux.js.org/docs/recipes/UsingImmutableJS.html) is littered with a long list of caveats precisely because there exists far too many not-immediately-obvious performance footguns available for one to shoot themselves with when taking up the task of carefully micro-optimizing all the expensive toJS and fromJS calls they'd have to make to build anything non-trivial. This is of course not to throw ImmutableJS under the bus, because it provides an excellent set of persistent data structures by any measure. It does have an uphill battle to fight to justify its adoption though, because it exists in an ecosystem that compels users to reject it for no fault of its own.
I haven't even gotten started on the developer ergonomics of using non-native data-structures, which since ES6 has taken a (relative) nosedive, because of all the syntactic sugar that was introduced for the built-in data structures.
Contrast this with the ClojureScript ecosystem, in which the use of efficient persistent data structures is rarely ever a question, because that's the default, and every third party library and application written in CLJS can just take the associated performance benefits for granted.
P.S. Apologies for the rant. I've been switching back and forth between CLJS projects and JS projects for the past year, and this has always been my biggest pet peeve with the state of functional JS. I'm actually quite happy with just about every other aspect of functional JS, to be honest, but I fear JS may never reach its full potential as a functional language because of this core defect.
Its not just night & day, closurescript > js; a lot of very good stuff exists there, but immutable by design isn’t a silver bullet that magically makes problems go away, and it (cljs that is) does introduce its own problems.
Practical examples of where cljs is tangibly a better choice is probably more useful to people.
For example, I personally have fought a cljs modal that kept an open channel waiting on user input; but when the modal closed, the channel was still there waiting and when it reopened there were now two channels, both getting input events, doing twice the work. Bad design, and not fp at all.
How did immutable structures help? Not at all. The author just used a global state atom to store everything anyway.
I’m just saying; yes, in clojure immutable is on by default, and that is good... but I don’t think I’ve observed, particularly, better code & lower bug rates in the cljs code I’ve worked on as a result...
I was only lamenting what I consider to be the only major deficiency in JS when it comes to writing functional code, one that I haven't really found any viable way to work around, despite being quite happy with most other aspects of the overall experience of writing functional JS.
I've found the free drip-fed email course helpful. The free video content is also well presented, as are Eric's talks at Clojure conferences on YouTube. I plan to subscribe, but the video content is also downloadable individually if you want to skip the free tips and offers in the intro emails: https://purelyfunctional.tv/courses/
With no offence, but what quality code/library/framework has he shared or produced?
Building composable abstractions https://www.youtube.com/watch?v=jJIUoaIvD20
Category Theory From the Universe Up https://purelyfunctional.tv/courses/category-theory/
Data modelling in Clojure https://purelyfunctional.tv/courses/modeling-solitaire-in-cl...
To me instruction like this is worth more than a library with a few thousand stars on GitHub.
The FP industry could benefit from more programmer-educators that don't actively obfuscate concepts in mathspeak. (That sometimes has its place too, though, and there are other resources that do a good job of deciphering monads etc. without heavy metaphor, like Haskell Programming From First Principles: http://haskellbook.com/.)
There are two main reasons I think. First, you originally call the developer well-respected and then when pressed reveal that you really meant "personally like". This comes off as double speak and feels like you just lied in the first comment.
Second and what seals the deal for me, is that you double down on the double speak and argue that your stance is "more valuable". This comes off as highly defensive.
I certainly can't speak for your intentions or the actual content of your messages, but I'm assuming the charitable thing and guess that you might be unwittingly speaking in a way that's putting people off.
That said, these are just my personal gut reactions, so YMMV.
I recognise that it's easy online to mistake enthusiasm for overselling, and that wasn't my intention at all.
I have no stake in this other than to encourage people to give Clojure a try (I just enjoy the language and want to see it grow). I shared my perception that the site owner is well-respected (based on the community's reactions to blog posts, forum posts, and talks), and gave my own reasons (the education style works for me and he speaks with clarity and authority on a topic that can often be mired in academic concerns).
The good thing is that others teach Clojure actively if you're looking for a less job-oriented marketing pitch. For example, https://lambdaisland.com/ is worth learning from (and run by a developer with a popular Clojure project called Chestnut, if that matters to you), http://www.4clojure.com/ and http://clojurescriptkoans.com/ are free practise sites, and the https://www.braveclojure.com/ and Living Clojure books were helpful to me when getting started.
> It can be the first step in a more fun and creative career.
This phrasing is everywhere in the ad. To me, it's just someone trying to make money from teaching a language. Nothing wrong with the teaching part but the hidden-yet-never-spelled-out-promises-because-of-legal-exposure feels very disingenuous and scammy to me.
I mean, maybe you can even do so in a C# shop, ClojureCLR is a real thing.
Clojure is a language that needs a lot of careful thought before being dropped into a Java shop. It's the complete opposite of Java in many respects and at some point that code will need to be maintained.
It is not without some career risks, but it can also open new doors.
Just pick one way to to do something, and make that the default in the language. This is why Python is one of the more popular languages today, you can go from 0 to relatively proficient in no time. No FP lang can claim the same thing. And people wonder why after 50 years FP hasn't completely taken off (unless you count javascript).
I can read 10 programs solving the same problem in clojure and they can look almost completely different. That is bug, not a feature, I'm sorry. Having "freedom" to do things in different ways just adds cognitive load. We already deal with enough option fatigue in our daily lives, why make it a core part of a language?
Multiple ways to do something is a feature, and there's definitely multiple ways to do things in python: metaclass vs class decorators lambdas vs explicit functions (basically the same thing as your no hash example) map/reduce/filter vs list/generator comprehensions
I am sort of afraid that I might end up in similar situation if I would need to change jobs. At my current company I worked 1/2y in Java, 1y in Clojure, 1y in Python, 1/2y in C#, 2y in mix of javascript, groovy, ansible and chef-scripts. There seems to be GO on a horizon.
This means that all the well earning "Senior Developer - requires 5 years experience with swing" will probably be out of reach.
But as they say, you shouldn't call yourself $LANGUAGE-programmer anyway, so I hope I will be able to sell myself as the "7 years of devops/process-automation", rather than "2-year Node.js experience" guy :-)
Except for the very elegant syntax. Your Clojure code will be at least half the size of equivalent JS code, probably much less than that. And without pulling in explicit dependencies on ImmutableJS, React and others.
Projects with less code have less bugs and are more fun to think about, in general. That's something extra Clojure brings to the table.
Like, it's true you can implement a Strategy with a function. And yet the majority of libraries on my current Clojure project don't let you supply a function as a strategy even when it'd make sense. Because none of the authors have made that design decision. Also your commands don't support undo. That'd take _two_ functions. And you know how you might communicate what those _two_ functions taken together do? You could use patterns! So somehow, just having a language feature doesn't MAGICALLY lead one to make one design decision over another. It didn't in the 90s, it didn't in the 00s, and it doesn't now.
Looking forward to 2025's version of this snarky article in Julia or whatever though.
But not as much as Clojure having no good implementation of the age old 'Readable Stacktrace' design pattern. :)
Well it does, a little bit. If you implement them by aligning rocks on a sunny beach, it might take a tad longer and more prone to bit rot. Java may as well be that for some people.
I've worked with and around Clojure developers for years now, and no one talks about traditional design patterns. Design patterns add complexity, not simplicity, to a language. By contrast, Clojure solutions are relatively simple. Design patterns often evolve in languages where you struggle against the language's design to express a solution. When a language strips away those obstacles, pre-existing patterns become less necessary.
– Peter Norvig
Alternatively, even if one agreed with the quote, it isn't very useful: it is simply another way to state that no programming language does everything.
No, but see https://blog.plover.com/prog/design-patterns.html and the follow-up https://blog.plover.com/prog/johnson.html
They're both a bit long, so here's what I think is the key quote (from the follow-up):
[W]hen pattern P applies to language L, then, to the extent that some programmer on some project finds themselves needing to use P in their project, the use of P indicates a deficiency in language L for that project.
The absence of a convenient and simple way to do P in language L is not always a problem. You might do a project in language L that does not require the use of pattern P. Then the problem does not manifest, and, whatever L's deficiencies might be for other projects, it is not deficient in that way for your project.
This should not be difficult for anyone to understand. Perl might be a very nice language for writing a program to compile a bioinformatic data file into a more reasonable form; it might be a terrible language for writing a real-time missile guidance system. Its deficiencies operate in the missile guidance project in a way that they may not in the data munging project.
But to the extent that some deficiency does come up in your project, it is a problem, because you are implementing the same design over and over, the same arrangement of objects and classes, to accomplish the same purpose. If the language provided more support for solving this recurring design problem, you wouldn't need to use a "pattern". Consider again the example of the "subroutine" pattern in assembly language: don't you have anything better to do than redesign and re-implement the process of saving the register values in a stack frame, over and over? Well, yes, you do. And that is why you use a language that has that built in. Consider again the example of the "object-oriented class" pattern in C: don't you have anything better to do than redesign and re-implement object-oriented method dispatch with inheritance, over and over? Yes, you do. And that is why you use a language that has that built in, if that is what you need.
By Gamma, Helm, Johnson, and Vlissides' own definition, the problems solved by patterns are recurring problems, and programmers must address them recurringly.
If these problems recurred in every language, we might conclude that they were endemic to programming itself. We might not, but it's hard to say, since if there are any such problems, they have not yet been brought to my attention. Every pattern discovered so far seems to be specific to only a small subset of the world's languages.
So it seems a small step to conclude that these recurring, language-specific problems are actually problems with the languages themselves. No problem is a problem in every language, but rather each problem is a red arrow, pointing at a design flaw in the language in which it appears.
The way to achieve this is emphatically not adding patterns to the language spec. Instead, you want small and simple languages that can grow—languages flexible enough to add new abstractions that let you write code the way you want.
"Growing a Language"[1] by Guy Steele expounds on this and, as a bonus, is the single best technical talk I've ever watched. Highly recommend watching it[1] or, if you're pressed for time, reading the transcript[2].
[1]: https://www.youtube.com/watch?v=_ahvzDzKdB0
[2]: https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf
It takes a while to overcome education and job conditioning and take hold. At first, you're like, ha ha, this is not real. Then, you're like, well, this isn't real but it can inspire some changes in the way I program. Then, you notice yourself doing stupid busy-work in Java or Perl and think, hmm. Hmm. Then, it's like, ya know, this entire project I'm working on would have been sooo much simpler in Clojure. Then, you're like, screw it, I'll just do this minor JDBC data migration or little process-simulation webpage in Clojure. whee! And finally: there is just no other way. Bring it on! Any task, it comes out better and sooner and much, much smaller in Clojure.
But those stages may take years.
That line of reasoning never ceases to be amusing by how silly and self-defeating,particularly as those who make those claims try to pin ignorance on others while manifesting their own ignorance. They seem to be entirely oblivious to the fact that the main goal and usefulness of patterns is to communicate intent and provide semantics and use a common language to describe common problems. A function is a function but a strategy pattern isn't a state pattern. Those who miss this fundamental difference are totally oblivious to what design patterns actually are.
No, that's the role of programming languages. If you can't do it in your programming language of choice, you aren't using a high-level enough one.
What a truism. Programming languages exist to implement functionalities outside of the core language. So what? Does this mean that software projects aren't designed, and parts of the design may not be shared and integrated in other projects? Those working on software projects still need to discuss design issues, and it's easier and more efficient to assign names to recurring patterns. Those who don't understand this issue and opt to manifest their ignorance by criticizing the existence of patterns are simply fools who try to feign competence by ridiculing the consequences of their own ignorance.
They do convey a precise meaning. You simply fail to understand it, and even make it your point to complain about something you systematically fail to understand where you stand on your dunning-kruger corner.
Then formalize it. Mathematically.
Accusations of trolling are completely unwarranted.
Adding "it's just..." in front of functional patterns doesn't make them non-patterns.
Patterns are named ideas on how to approach/solve similar problems.
They are not tied to OO programming, Java, they are not good or bad per-se; it's just a cookbook with recipes that have names.
Otherwise nice format, I like it.
don't you mean.... the arithmetic mean?
https://en.wikipedia.org/wiki/Arithmetic_mean
Oh my god, and people bother themselves calling the addition o sequences of numbers as summation.
https://en.wikipedia.org/wiki/Summation
Where does this madness end? These morons even bothered themselves forming whole fields in mathematics by studying adding numbers
https://en.wikipedia.org/wiki/Series_(mathematics)
Why is humanity so wrong about such a simple concept? Why is everyone bothered to give dedicated names to concepts you fail to understand?
I insist: You only need “patterns” when you can't articulate your ideas precisely in a formal language, such as a programming language or mathematics.
And how you go about developing those "libraries"?
By writing code using the programming language, isn't it?
Just because C++ and Java and Python and many other programming languages added support for iterators and generators it doesn't make them out to be less of a design pattern. Heck, supporting emcapsulation and composition and information hiding doesn't make a facade pattern, as well as its concept and the semantics of implementing one, magically disappear.
This fundamental fact regarding design patterns is what these self-important fools miss. While they are too busy grandstanding with their criticism, they are actually showing off their ignorance and demonstrating they don't even understand the basic problems solved by design patterns, which are orthogonal to programming languages and their features.
Funny you should mention that. What does "state pattern" communicate about the design, that "state machine" does not? Because state machine is a well-established term, applicable across paradigms.
And what can you communicate with "strategy pattern"? I mean, pick a design for solving a problem (any problem), and explain the design in writing using the words "strategy pattern". I bet I can rephrase it to be at least as simple, and use common-language terms that predate the "strategy pattern".
I've heard people try to take this stance before, that "design patterns are a way to communicate your designs", but to me, it doesn't hold water. How do you communicate your designs simpler, with them?
The condescending tone of the post is completely unwarranted, especially since it glosses over the real limitations of procedural values as abstraction building blocks, which don't magically go away when you rename them from “objects” to “functions”. In no particular order:
(0) There is no modular way to distinguish procedures that meet a nontrivial specification from all other procedures. Guaranteeing that a procedural argument never takes a silly value requires whole-program analysis, which doesn't scale.
(1) There is no modular way to communicate the fact that two or more procedures manipulate similar or related data structures. Try implementing abstract data types with binary operations (e.g. mergeable heaps) as objects.
(2) There is no modular way to control which procedures exchange information with one another. Say Foo is designed to communicate with Bar. I can replace Bar with a type-compatible but ill-behaved impostor Qux. As far as Foo can tell: https://az616578.vo.msecnd.net/files/2016/09/26/636105299923...
The cure for this illness is using abstract data types, not objects.
Without abstraction enforcement mechanisms, an abstraction you have correctly implemented can still misbehave, because someone else has tampered with its internal representation.
Regardless, Rich Hickey's last talk (last month at Clojure Conj) offered a counter argument, why ADTs are usually not the best way to organize software (as part of his larger discussion against static typing).
It depends on context
> why ADTs are usually not the best way to organize software
He clearly hasn't proven anything about his code.
Is that a bad thing? It seems like many people are able to write software that works well and gets the job done without "proving anything" about their code in the way you're describing. Personally, I'm fine with not proving anything if I can deliver quickly and everything works, but I'd like to be convinced otherwise if there's real value there.
Let's try not to make inflammatory statements.
It’s a mirage to which many have succumb but like in the desert most died of thirst trying to catch it.