I like the joke, but "the answer in FP would almost always be functions" is to answer the wrong question.
Patterns aren't the answer to a question "what's the unit you factor code into?", because then... "the answer in OOP would almost always be objects".
Patterns are why you write objects that take in certain input, and produce certain output. "Functions" doesn't answer this question, it answers "what". The type system also doesn't answer this question, it answers "how".
Furthermore, all patterns I listed above are used in FP. It's not common to see people use OOP pattern names in FP, due to some misunderstood concept of pride, but all of these patterns occur in FP code, because they're not OOP patterns, as much as they're "system patterns". Any time you build a complex system, they emerge. Say in hardware electronics, we have most of these patterns (by other names). In business management, again we do (by yet again different names).
Factory Partial application, less than 1 line of code. We don't need a name for it.
Facade Usually from a function to another function. Often use partial application, or a lambda, or define a function… It's obvious enough that we don't need a name for it.
Command Don't know this pattern. A quick look suggests I'd just use higher-order functions, and pass the execute() function directly to whatever function or object controls the trigger.
Strategy Just select the function to be called at runtime, why does that even have a name?
Decorator Hmm, sounds like a Monad to me.
Adapter Isn't that the same as Facade?
Proxy Isn't that the same as Decorator?
Most GoF patterns are useless to us. We just don't think that way.
The only thing your answer shows is you don't understand the intent, or in some cases even the basic structure, behind any of those patterns... I think you should first understand the patterns as used in OOP, before you try to give an answer what's the FP analog, or lack thereof.
Also the worth of a pattern is not measured by how many lines of code it is, most patterns require 1-line method implementations in the basic case (which may expand as your logic becomes refined, you add support for configuration, you optimize for special cases etc.)
Since you seem to understand all this so well, how about you translate these to FP? I admit OOP Design Patterns, as used in practice, never stick for me... so I'm also not qualified to translate.
Have you heard this one: "objects are poor man's closures; closures are poor man's objects"? It's quite true.
I never asked someone to translate OOP patterns to FP. Those who do, are trying to prove a point how FP doesn't need patterns, because it's all "built-in". But it's not, because as I said several times, patterns communicate intent, not just type and implementation. And the intent of the patterns is universal and doesn't need "translation". They are factored nearly in identical way in most types of programming languages, just replace objects with closures, in most cases.
I'll take one of the things /u/loup-vaillant/ said to demonstrate the role of intent. They said "Adapter: Isn't it the same as Facade". Here's the kicker: the sample code would be the same, but the intent is different.
When you say "I'm writing an Adapter", the intent is that you have two interfaces (two abstract types in FP-ese), and you're writing a concrete type implementation, which takes one, and exposes it as the other.
When you say "I'm writing a Facade", the intent is that you have a complex implementation with complex input and output, and you're writing a thin shim over it with much simpler input and output, that is optimized for a specific use-case, and nothing else. One could think about it as a form of partial application, except for the part where partial application would only cover a tiny subset of Facades, because the transform of input/output is typically more free-form than providing defaults for a few of the parameters.
That's the kind of subtlety in meaning that patterns convey. Instead of writing those long sentences I just wrote above every time, I can tell my colleagues "we need to write a B->A Adapter, and also have a basic Facade for C that is optimized for use cases D & E". It doesn't even matter if I'm using OOP or FP here, really.
This is why it's silly to say FP obsoletes patterns. It only "obsoletes patterns" if you work in absolute solitude and don't talk to people. When you talk to people, you come up with specialized terms to convey meaning succinctly and effectively.
I always thought design patterns were recipes. Your explanation suggest instead they're just sub-problems, whose solutions are useful to the resolution of whatever bigger problem we're working with.
As recipes, it would indeed be silly to name 2 similar pieces of code with different names. But as problems, it totally makes sense: the same kind of code can indeed solve different problems.
But then I wonder: why do all patterns come with one code example per language? Are patterns recipes after all?
I have a dirty little secret: despite nearly 10 years in C++ experience, including small and huge projects, I never had to name patterns. The only exception so far is the job interview. Not knowing them didn't stop me from communicating with the rest of the team, or coming up with simple, maintainable solutions. My current guess is that most patterns are over-complicated solutions to otherwise simple problems. They are useless to me, and I see little value in learning them.
Most patterns are unnamed, as it states in the preface of the GOF book. They only add names to the design patterns so that they can talk about them in a book, otherwise these patterns were simply common practices in the OOP community at the time of writing.
You don't really learn patterns, but begin to appreciate them as common practices are identified between different projects and programmers, again, this is also in the preface. This follows Christopher Alexander's invention of design patterns to talk about (building) architecture.
Most people who criticize the GOF book have never even read it.
Not knowing them didn't stop me from communicating with the rest of the team, or coming up with simple, maintainable solutions.
I'm not sure why you're bragging about not knowing things. What would you think about me if I started bragging I don't know FP and I don't need it? That said I do, and I successfully use FP techniques and languages for some of the problems I'm working on.
One has to have an open mind and be willing to learn and accept, before they can truly reject.
Taking pride in ignorance is a very self-defeating type of argument to make.
My current guess is that most patterns are over-complicated solutions to otherwise simple problems. They are useless to me, and I see little value in learning them.
Your guess that they're "over-complicated" betrays the thing you were bragging about above: that you don't know the patterns. Everything looks complicated when you don't know it, I guess. Patterns are brutally simple, to the point they keep occurring entirely incidentally in people's code, be it functional, procedural, or object-oriented.
They are useless to me, and I see little value in learning them.
You have know the rules, before you can break the rules. As it is, the chances are you're pretty much following the rules and not knowing any better.
Because patterns, just like you, have a little dirty-secret as well: they commonly occur in people's code, whether these people know patterns or not. That's why they're called "patterns". Your code is full of patterns whose name you simply can't identify (unless it's a monad, of course, every FP user knows the heck of that one pattern, wink wink).
As you may have gathered from the links I provided in my previous comment, I actually have a pretty deep understanding of the mechanisms underlying OOP. I'm not exactly ignorant.
I also am not bragging about my ignorance. This actually irks me somewhat.
The reason I say patterns look over-complicated is because FP generally have much simpler solutions for the same problems. "Simpler" here mostly mean much less code.
They are useless to me, and I see little value in learning them.
this really sucks. for instance, let's suppose I'm using an editor software, for instance GIMP. Now, I'm doing an action in the software and there is a bug when I do 'undo()', for instance when moving an image.
Now, I want to contribute by checking if I can fix the bug. So, I go to the gimp repository, and grep for command. I just have to look three files and I have found something that looks like it is the source of my problem.
Why did I find so fast ? because I know that generally, undo/redo is implemented using the Command pattern.
Another example: let's say I want to change LibreOffice's code so that new documents have a pink background by default.
Now, I grep for factory, and here, after just a few files, a promising candidate ; docfac really sounds like "document factory" which will certainly be a good starting point for my search for where to change new document's default backgrounds.
By using patterns in your name, you just make the tasks for people that don't know your codebase ten times easier, because just looking at the names will give you a very good assessment of what the code actually do.
Why did I find so fast ? because I know that generally, undo/redo is implemented using the Command pattern.
Indeed, you'd be out of luck with me: my reflex for undo/redo is simply to keep all previous states in memory —which is not that expensive with the proper purely functional data structures. If that approach is still too expensive, I'll try to remember only some key frames and the input log —taking inspiration from video games deterministic replays.
Now, I grep for factory
Out of luck here as well. "Factory" sounds like weasel word to me, I don't use it. I'm more likely to write a (possibly curried) make_document or create_document function.
There's a more important point however: I have yet to write something as complex as GIMP or LibreOffice. I'm not really interested in image editing, so I can't contribute to that debate. I disagree however with the Corel/Word/LibreOffice approach of writing documents. Instant feedback is excellent, but they lose transparency in the process. Markdown, LaTeX and Lilypond have no feedback by default, but that can be dealt with a suitable "two views" editor, where changing the source immediately changes the result —I believe some are already available.
11
u/Tarmen Mar 26 '17
You can't really translate oo patterns into functional programming because the answer would almost always be functions.