Does functional programming replace GoF design patterns?
Asked Answered
H

30

1149

Since I started learning F# and OCaml last year, I've read a huge number of articles which insist that design patterns (especially in Java) are workarounds for the missing features in imperative languages. One article I found makes a fairly strong claim:

Most people I've met have read the Design Patterns book by the Gang of Four (GoF). Any self respecting programmer will tell you that the book is language agnostic and the patterns apply to software engineering in general, regardless of which language you use. This is a noble claim. Unfortunately it is far removed from the truth.

Functional languages are extremely expressive. In a functional language one does not need design patterns because the language is likely so high level, you end up programming in concepts that eliminate design patterns all together.

The main features of functional programming (FP) include functions as first-class values, currying, immutable values, etc. It doesn't seem obvious to me that OO design patterns are approximating any of those features.

Additionally, in functional languages which support OOP (such as F# and OCaml), it seems obvious to me that programmers using these languages would use the same design patterns found available to every other OOP language. In fact, right now I use F# and OCaml every day, and there are no striking differences between the patterns I use in these languages vs. the patterns I use when I write in Java.

Is there any truth to the claim that functional programming eliminates the need for OOP design patterns? If so, could you post or link to an example of a typical OOP design pattern and its functional equivalent?

Huang answered 29/11, 2008 at 20:8 Comment(11)
You might look at the article by Steve Yegge (steve-yegge.blogspot.com/2006/03/…)Jiggerypokery
There's another where Yegge specifically writes about design patterns being functional programming in disguise: sites.google.com/site/steveyegge2/singleton-considered-stupidDarrel
"the book is language agnostic and the patterns apply to software engineering in general" - it should be noted that the book disagrees with this claim, in the sense that some languages don't need to express certain things like design patterns: "Our patterns assume Smalltalk/C++-level language features, and that choice determines what can and cannot be implemented easily [...] CLOS has multi-methods, for example, which lessen the need for a pattern such as Visitor (Page 331)." (page 4)Carlinecarling
Also keep in mind that many design patterns aren't even necessary in sufficiently high level imperative languages.Blate
@cibercitizen1 duck-typed languages with support for higher order functions and anonymous functions. These features supply much of the power that a lot of design patterns were meant to provide.Blate
whoever write the quote deserves to be ignored based on his attitude. Compering OOP with Functional programming is like comparing perl with java or assembler with c#. The true is that neither is better. Actually they could work well together to deliver best value. Each tools simply should be used to solve right problems, and that is all. Claiming that functional programming is solution to all badness in software codebases is naive and in-mature.Transeunt
Related: mishadoff.com/blog/clojure-design-patternsCailly
There are design patterns in many areas of software engineering, not just GoF, and they usually complement each other rather then replace. see github.com/DovAmir/awesome-design-patternsPhiphenomenon
there are lots of patterns, but there are only about 25 oo design patterns.Mistiemistime
This is an interesting, highly voted question that clearly shows effort and research and that has also received great answers. This "opinion based" close reason is killing me :(.Grieve
@DanielJour - I have been looking for something like this for awhile! Thank you! I wish you had an actual answer. I'd vote you up!Babineaux
A
1173

The blog post you quoted overstates its claim a bit. FP doesn't eliminate the need for design patterns. The term "design patterns" just isn't widely used to describe the same thing in FP languages. But they exist. Functional languages have plenty of best practice rules of the form "when you encounter problem X, use code that looks like Y", which is basically what a design pattern is.

However, it's correct that most OOP-specific design patterns are pretty much irrelevant in functional languages.

I don't think it should be particularly controversial to say that design patterns in general only exist to patch up shortcomings in the language. And if another language can solve the same problem trivially, that other language won't have need of a design pattern for it. Users of that language may not even be aware that the problem exists, because, well, it's not a problem in that language.

Here is what the Gang of Four has to say about this issue:

The choice of programming language is important because it influences one's point of view. Our patterns assume Smalltalk/C++-level language features, and that choice determines what can and cannot be implemented easily. If we assumed procedural languages, we might have included design patterns called "Inheritance", "Encapsulation," and "Polymorphism". Similarly, some of our patterns are supported directly by the less common object-oriented languages. CLOS has multi-methods, for example, which lessen the need for a pattern such as Visitor. In fact, there are enough differences between Smalltalk and C++ to mean that some patterns can be expressed more easily in one language than the other. (See Iterator for example.)

(The above is a quote from the Introduction to the Design Patterns book, page 4, paragraph 3)

The main features of functional programming include functions as first-class values, currying, immutable values, etc. It doesn't seem obvious to me that OO design patterns are approximating any of those features.

What is the command pattern, if not an approximation of first-class functions? :) In an FP language, you'd simply pass a function as the argument to another function. In an OOP language, you have to wrap up the function in a class, which you can instantiate and then pass that object to the other function. The effect is the same, but in OOP it's called a design pattern, and it takes a whole lot more code. And what is the abstract factory pattern, if not currying? Pass parameters to a function a bit at a time, to configure what kind of value it spits out when you finally call it.

So yes, several GoF design patterns are rendered redundant in FP languages, because more powerful and easier to use alternatives exist.

But of course there are still design patterns which are not solved by FP languages. What is the FP equivalent of a singleton? (Disregarding for a moment that singletons are generally a terrible pattern to use.)

And it works both ways too. As I said, FP has its design patterns too; people just don't usually think of them as such.

But you may have run across monads. What are they, if not a design pattern for "dealing with global state"? That's a problem that's so simple in OOP languages that no equivalent design pattern exists there.

We don't need a design pattern for "increment a static variable", or "read from that socket", because it's just what you do.

Saying a monad is a design pattern is as absurd as saying the Integers with their usual operations and zero element is a design pattern. No, a monad is a mathematical pattern, not a design pattern.

In (pure) functional languages, side effects and mutable state are impossible, unless you work around it with the monad "design pattern", or any of the other methods for allowing the same thing.

Additionally, in functional languages which support OOP (such as F# and OCaml), it seems obvious to me that programmers using these languages would use the same design patterns found available to every other OOP language. In fact, right now I use F# and OCaml everyday, and there are no striking differences between the patterns I use in these languages vs the patterns I use when I write in Java.

Perhaps because you're still thinking imperatively? A lot of people, after dealing with imperative languages all their lives, have a hard time giving up on that habit when they try a functional language. (I've seen some pretty funny attempts at F#, where literally every function was just a string of 'let' statements, basically as if you'd taken a C program, and replaced all semicolons with 'let'. :))

But another possibility might be that you just haven't realized that you're solving problems trivially which would require design patterns in an OOP language.

When you use currying, or pass a function as an argument to another, stop and think about how you'd do that in an OOP language.

Is there any truth to the claim that functional programming eliminates the need for OOP design patterns?

Yep. :) When you work in a FP language, you no longer need the OOP-specific design patterns. But you still need some general design patterns, like MVC or other non-OOP specific stuff, and you need a couple of new FP-specific "design patterns" instead. All languages have their shortcomings, and design patterns are usually how we work around them.

Anyway, you may find it interesting to try your hand at "cleaner" FP languages, like ML (my personal favorite, at least for learning purposes), or Haskell, where you don't have the OOP crutch to fall back on when you're faced with something new.


As expected, a few people objected to my definition of design patterns as "patching up shortcomings in a language", so here's my justification:

As already said, most design patterns are specific to one programming paradigm, or sometimes even one specific language. Often, they solve problems that only exist in that paradigm (see monads for FP, or abstract factories for OOP).

Why doesn't the abstract factory pattern exist in FP? Because the problem it tries to solve does not exist there.

So, if a problem exists in OOP languages, which does not exist in FP languages, then clearly that is a shortcoming of OOP languages. The problem can be solved, but your language does not do so, but requires a bunch of boilerplate code from you to work around it. Ideally, we'd like our programming language to magically make all problems go away. Any problem that is still there is in principle a shortcoming of the language. ;)

Abeyant answered 29/11, 2008 at 23:6 Comment(52)
"I don't think it's a particularly controversial viewpoint to say that design patterns in general only exist to patch up shortcomings in the language" It's not controversial. It's false. Design Patterns describe solutions to problems. Nothing to do with language shortcomings in any way.Organ
Design patterns describe general solutions to basic problems. But that's also what programming languages and platforms do. So you use design patterns when the languages and platforms you are using do not suffice.Tee
S.Lott: They describe solutions to problems which exist in a given language, yes. There is no Command design pattern in FP languages, because the problem it tries to solve does not exist. Which means that they solve problems that the language itself can't solve. That is, shortcomings in the languageAbeyant
The monad is a mathematical concept, and you're stretching it with your classification. Sure, you can view functions, monoids, monads, matrices or other mathematical concepts as design patterns, but those are more like algorithms and data structures ... fundamental concepts, language independent.Vicereine
I agree that monads are in fact a mathematical concept but in pure functional programming they are a design pattern for encapsulating mutable state. But if you don't want to except that except that STM is a pattern for managing concurrency.Lac
Sure, monads are a math concept, but they are also a pattern. The "FP pattern" of monads is somewhat distinct from the math concept of monads. The former is a pattern used to get around certain "limitations" in pure FP languages. The latter is a universal mathematical concept.Abeyant
Could you elaborate more on the "singletons are generally a terrible pattern to use"? I'm really curious, no flame intended.Applesauce
Note that monads in Haskell are used for other things than mutable state, for example for exceptions, continuations, list comprehensions, parsing, asynchronous programming and so on. But all these application of monads could probably be called patterns.Unavoidable
zarzych: Not much space here, you may want to create a separate question for this. Anyway, google for singletonitis or "singletons considered harmful". The short version is that singletons make dependencies implicit, can't be tested and overconstrain your code. Why can't I create more than 1 logger?Abeyant
olavk: Yeah, I agree. I only mentioned mutable state because that's a good example of something that's too simple to need a "pattern" in imperative languages. But I agree, all the other uses of monads would also be considered patterns.Abeyant
jalf: How is a Monad a design pattern if it is a type class you can define instances of? If Monad is a design pattern then numbers should also be design patterns in Haskell by that logic. In OO languages you can't just implement the "abstract factory" interface and expect that implementation to actually be an abstract factory... Also I don't see how a Singleton is even applicable to a functional language, there is no notion of objects so how can you talk about Singletons in FP?Wicklow
Languages are what they are because they present a particular paradigm. That an OOP language requires a design pattern to solve certain problems is not necessarily a shortcoming of the language. Rather, languages take a particular paradigm (i.e. OOP or FP), and seek to represent that paradigm in the most complete way possible while still being small enough for a programmer to carry around in his head. Suggesting that a language must encompass all problems belies the fact that some people may not wish to work in your problem space (preferring game programming, for example).Thoroughwort
Another example of how design patterns handle shorcommings in the language: In java you need the Observer pattern. However since C# has event / listeners right in the language you don't need it. So that is just a sample of two pretty similar languages and still a design pattern is irrelevant in the other.Inaugurate
"In (pure) functional languages, side effects and mutable state are impossible, unless you work around it with the monad "design pattern", or any of the other methods for allowing the same thing." I spend a lot of time destroying this bullshit myth. This is not enjoyable. It's disheartening to see it pop up again with appraisal.Slighting
S. Lott. Here are a couple of interesting riffs on the theme that design patterns are about language flaws: c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeatures norvig.com/design-patterns I notice the Norvig piece is mentioned several times below. It's quite a respected article and he argues the specific point that you are arguing against. I reckon you can't declare it as false without engaging with it's arguments in a bit more depth.Beata
@Tony: it's a simplification. What about it bothers you, exactly? @Rubendv: Singletons aren't applicable to functional languages, which is pretty much my point.Abeyant
It's not a simplification. It's simply false. That is what bothers me, but not as much as it bothers students who are trying to learn and are misled by these statements. It takes a lot of effort to back them out of it. At least, this has been my experience teaching. You are better off not saying anything, than to say a false statement that masquerades as a useful simplification (it seems you have even tricked yourself here). I don't doubt your sincerity in your efforts to help, but be aware that it's easy to accidentally do quite the opposite, which you have done.Slighting
@Tony: could you explain, instead of simply saying I'm wrong? How do pure FP languages allow mutable state and side effects then?Abeyant
@jalf: There are several things wrong. First, FP languages do not allow mutable state and side-effects. At All. Languages such as Haskell use an IO type constructor to "tag effects" by wrapping values that require a RealWorld argument. Such functions are explicitly not side-effecting. You'll notice no use of the word "monad" here. Here is a slide where I explicitly address this issue projects.tmorris.net/public/what-does-monad-mean/artifacts/1.1/…Slighting
@jalf: Second, other languages like Clean use a technique called uniqueness typing, which is different altogether to the IO type constructor.Slighting
@jalf: In Haskell, the IO type constructor just happens to be a monad. It is also: a covariant functor, a pointed functor, an applicative functor and many (MANY) other things. That it is a monad is completely irrelevant to the matter of tagging effects or, as you (inappropriately) mention, "allowing mutable state/side-effects."Slighting
@jalf: In other words, a monad has absolutely nothing at all to do with effect tracking. Indeed, I can think of a bazillion monads that pop up in every day programming that are completely departed from I/O. These monads occur in all sorts of programming languages, including those that explicitly depart from the FP thesis.Slighting
@jalf: You also state, "The \"FP pattern\" of monads is somewhat distinct from the math concept of monads. The former is a pattern used to get around certain "limitations" in pure FP languages. The latter is a universal mathematical concept." This kind of disinformation explicitly reinforces a horrible misunderstanding of the concept both on your behalf and your readers.Slighting
@jalf: That your incorrect and misleading explanation has so much appraisal is very disappointing. I hope you understand this, rather than take my protests personally. I know you meant well, but you unintentionally undo a lot of hard work. There is simply too much incorrect information to continue addressing it all here, so I hope you get a taste of what I wish to convey.Slighting
@jalf: Lest you offer resistance to this short explanation (which I am happy to expand if you have questions), then do realise that you have just become a student for whom it is "incredibly difficult to back out of a gross misunderstanding." In other words, you might get an insight into the point I wish to make regarding educational progress.Slighting
@Tony Morris I understand the upset caused by disregarding Monads as "just for x", do you disagree with the basic premise that Monads (and Arrows and other FP constructs) are, in a way, design patterns for FP? I certainly feel like some FP Monads are patterns, and Monads themselves are also a pattern. FP is just very powerful in that it typically allows one to bundle a pattern up into a reusable component better than most OOP languages. Like the Maybe monad is very close to the Null Object pattern, but generally applicable to any type.Trudge
@Tony Morris Haha, short and to the point. Thanks for your posts, information on advanced FP concepts is so hard to come by and usually conflicting and difficult to wade through.Trudge
Hello again CodexArcanum, no I don't have a slide show on Arrows, though I recently gave a talk on HXT, which is an Arrow-based parser.Slighting
Monads are not used for "dealing with global state" but in fact avoiding global state: they limit the manipulation of that state to only the specific functions within that monad, and those functions at run time have access only to the specific state provided by the caller. This could all be replicated by merely adding arguments to functions, and that is in fact the exact internal implementation: go through the Functional Parsers chapter of Graham Hutton's Programming in Haskell, where he teaches you to implement a monad (though he doesn't call it such explicitly) and you'll see just that.Allergen
@Tony: it would be a lot easier to "get an insight into the point you wish to make" if you actually made your point. So far, all I've seen is a rant about how misguided I am and a lament that I make your life more difficult. My point, as @Codex summarized it nicely, is that a monad in a FP language is a kind of "design pattern". A certain code structure that is rolled out to solve a series of common problems.Abeyant
As I said to @Curt above, you're too narrowly focused on FP semantics. Side effects are a fact of life. Every time you send a byte across a network connection, it is a side effect, whether or not it looks as such in your code. Every time you give the user a piece of information, you are modifying global state, whether or not it looks like that in your code. One of the many uses of monads (and I never claimed monads were the only way to do this, or that monads can be used for nothing else), is to allow you to express such unavoidable facts of mutability and of side effects.Abeyant
FP languages "naively" don't have a way to express such chains of events, where only one, universal and everchanging world exists. So FP programmers use various kinds of "design patterns" to express them. The I/O monad is one example of such a pattern.Abeyant
@jaif: You seem not to care about "whether or not it looks as such in your code"; my point is, in Haskell, it correctly does or does not look as such: you always know when you have potential side effects, and exactly what part of the state might be affected. The stateful contexts you work in (and that includes things as simple as having two statements in a row) are so natural to you that you realize you live within them no more than a fish knows he lives in water, and thus you don't realize how much more work it is to keep track of things in such a world.Allergen
In other words, the key is not that we can express state with monads, but that we don't always need to work in a stateful environment; we do so only when necessary, and thus simplify our lives.Allergen
@Curt: I don't see how any of this contradicts anything I said. I know that something as simple as one statement after another is a side effect. My point is very simple: it is something that we sometimes need to express, and it is natural to do so in an imperative language, and less natural in a FP language, so in FP languages we use certain "patterns" to do it. Just like other things are natural in FP languages, but require "design patterns" to be expressed in imperative languages. None of it contradicts what you said, and I'm not really sure what you're arguing against.Abeyant
@Abeyant I'd say "pure FP" rather than just "FP" in the context of side effects. When Tony Morris writes things like "FP languages do not allow mutable state and side-effects" he is going to confuse a lot more noobs than you did given that every mainstream FP language since Lisp has not only allowed but even required side-effects...Fewness
While MVC and others can still be used in FP languages, they're aren't exactly design patterns but rather architecture patterns. A design pattern only affects a few components while an architecture pattern touches the entire application.Protagonist
Monads aren't design patterns; Using Monads in pure languages to handle certain things that are handled with global state in other languages is a design pattern. I use monads in both Common Lisp and C, but (obviously) not for representing global state.Conrad
@Aidenn: and why is it not a design pattern? Because no one wrote a book about them with "design patterns" in the title? I don't see why it's any less of a design pattern than, say, a singleton is.Abeyant
@harrop: I'd argue that the "FP" part of those languages in fact does not allow side-effects, and it is only because they are multiparadigm languages that side-effects are incorporated into the language as a feature. Scala, F#, and certainly Lisp are prime examples.Donettedoney
One very important distinction is that "monad" is a first class notion in Haskell: there is a concrete entity (the Monad typeclass) which defines what a monad is, and you explicitly make instances of it. It isn't a "design pattern" so much as a normal library abstraction, just like Java's Comparable or Serializable.Mavismavra
@Abeyant I really enjoyed reading your answer. It's clear and to the point. Posting answers of this sort takes a lot of bravery because people are not really ready to change their point of view once it has been established for so many years. I would love to know your view on this answer that equates the Strategy Pattern to passing a function as a parameter. Do you think there is a 1:1 correlation between the two?Cuneiform
I liked your answer but your statement about monads is not correct - a monad is not a design pattern for dealing with global state. It's just a container which express the intention of doing something and it's pure. Every Haskell program runs that intention (returned by main) and that's how you get a working program. I came here because a fellow developer who is trying to learn found your answer and got confused. I would appreciate if you could reformulate the bit about monad. Maybe (no pun intended) "when you need to chain computations together use a monad"?Ardra
Calling a monad a design pattern is a fundamental misunderstanding of both Monads and design patterns. Using Monads is the exact opposite of using deign patterns. A design pattern is a (supposedly) healthy way to structure repetitive code to achieve some fairly similar programming task that you can't just abstract away. The purpose of using Monads in programming language is to avoid repeating this code. We use them to avoid design patterns. Talking about the "Monad design pattern" in FP is even worse than talking about the "variable assignment design pattern" in imperative languages.Nmr
'"when you encounter problem X, use code that looks like Y", which is basically what a design pattern is.' Well when the "pattern" is low level, it's more like an "Idiom". Yes FP has Idioms, but I don't think it really has design patterns.Outflank
Monads came first in Category Theory - it has a formal definition, it's a mathematical construct (like Monoid, SemiGroup, algebras, etc). Design patterns aren't based on formal mathematical constructs.Outflank
I really don't understand why "monad" or "monoid" can't be considered a design pattern while something equally abstract like "abstract factory" or "visitor" can. Because monad/monoid etc. unambiguously described in strict math terms (and can even be included in std library like in Haskell) while nobody knows what exactly people mean when they say "bridge" or "strategy"? Is that's it: if you can't unambigously express and reuse some repetitive stuff in a programming language only then it can be called a "design pattern"? If so then less "patterns" your language has the better it is.Categorical
You can argue that the concept of monad is not a design pattern (since it is a category theory concept), but for example the use of monads for asynchronous promises (like lwt for example) is something that very much fits the definition of a design pattern, isn’t it ?Oligarch
An equivalent of a Singleton in FP would be a closure. In fact, a closure is nothing else than a FP equivalent of a class, in my view. local variables in a closure over which functions within the closure are "closed-over" are the equivalent of attributes in a class. If I understand Singleton as a bunch of attributes and methods encapsulated inside an one singleton object - isn't a singleton nothing else than a global environment inside-out? Exactly that can be achieved by a closure!Complected
Isn't Currying actually a builder rather than a factory?Decameter
While I often let that analogy pass by, technically, saying "Monad is a design pattern" is like saying "Collection class is a design pattern". Monad is a concrete typeclass in haskell, and gets special treatment in other FP langs (with syntax sugar like for comprehension). Does Flyweight get special treatment in e.g. Java? No! You should have mentioned things like"Tagless Final", that is a design pattern.Flyleaf
How can I understand the quote in the book? Why for procedural languages, we might have included design patterns called "Inheritance", "Encapsulation" and "Polymorphism"? Aren't they features of object-oriented languages? How can they be included in procedural languages by us?Petropavlovsk
O
166

Is there any truth to the claim that functional programming eliminates the need for OOP design patterns?

Functional programming is not the same as object-oriented programming. Object-oriented design patterns don't apply to functional programming. Instead, you have functional programming design patterns.

For functional programming, you won't read the OO design pattern books; you'll read other books on FP design patterns.

language agnostic

Not totally. Only language-agnostic with respect to OO languages. The design patterns don't apply to procedural languages at all. They barely make sense in a relational database design context. They don't apply when designing a spreadsheet.

a typical OOP design pattern and its functional equivalent?

The above shouldn't exist. That's like asking for a piece of procedural code rewritten as OO code. Ummm... If I translate the original Fortran (or C) into Java, I haven't done anything more than translate it. If I totally rewrite it into an OO paradigm, it will no longer look anything like the original Fortran or C -- it will be unrecognizable.

There's no simple mapping from OO design to functional design. They're very different ways of looking at the problem.

Functional programming (like all styles of programming) has design patterns. Relational databases have design patterns, OO has design patterns, and procedural programming has design patterns. Everything has design patterns, even the architecture of buildings.

Design patterns -- as a concept -- are a timeless way of building, irrespective of technology or problem domain. However, specific design patterns apply to specific problem domains and technologies.

Everyone who thinks about what they're doing will uncover design patterns.

Organ answered 29/11, 2008 at 20:15 Comment(8)
That's pretty much what I expected, although I think there is some truth in the claim that functional programming can simplify existing patterns. For example, I find that I no longer need to pass around single-method interfaces when I can pass functions as values instead.Huang
Some design patterns disappear in languages having certain features. For example if you can pass function around, you don't really need the Command pattern anymore.Gristede
I am far from expert in FP or design patterns, but I think that some probably apply to lot of programming paradigms, even procedural ones. For example, the good old MV[C]. Separating view from business logic looks like a good idea, whatever the language, no?Esprit
MVC isn't OO design. It's architectural design -- that pattern applies pretty widely.Organ
@Princess: functional programming isn't necessarily simpler. In your example, yes. For other things, the jury's still out. But you have discarded a Java OO design pattern and adopted a FP design pattern.Organ
+1: I prefer this answer to Jalf's answer above. Although some design patterns address deficiencies in the language, not all do. For example, I'd hardly say that the "untying the recursive knot" design pattern addresses a deficiency in the language, it is just a useful idiom to loosen dependencies.Fewness
Java 8 will include closures aka anonymous functions aka lambda expressions. This will make the command design pattern obsolete for Java. This is an example of language shortcoming, no? They added a missing feature and now you don't need the design pattern.Etra
+1 for the closing sentence. Design patterns are meant to simplify programming and make the resulting programs more efficient, at what they are intended to do.Mystify
L
51

Brian's comments on the tight linkage between language and pattern is to the point,

The missing part of this discussion is the concept of idiom. James O. Coplien's book, "Advanced C++" was a huge influence here. Long before he discovered Christopher Alexander and the Column Without a Name (and you can't talk sensibly about patterns without reading Alexander either), he talked about the importance of mastering idioms in truly learning a language. He used string copy in C as an example, while(*from++ = *to++); You can see this as a bandaid for a missing language feature (or library feature), but what really matters about it is that it's a larger unit of thought, or of expression, than any of its parts.

That is what patterns, and languages, are trying to do, to allow us to express our intentions more succinctly. The richer the units of thought the more complex the thoughts you can express. Having a rich, shared vocabulary at a range of scales - from system architecture down to bit twiddling - allows us to have more intelligent conversations, and thoughts about what we should be doing.

We can also, as individuals, learn. Which is the entire point of the exercise. We each can understand and use things we would never be able to think of ourselves. Languages, frameworks, libraries, patterns, idioms and so on all have their place in sharing the intellectual wealth.

Liebfraumilch answered 1/1, 2009 at 20:59 Comment(3)
Thank you! this is what patterns are about—"conceptual chunking" to lower cognitive burden.Bergmann
And Functional Monads definitely belong in this discussion.Aeschylus
@RandallSchulz: language features (and their idiomatic use, of course) would also fit well into the category of "conceptual chunking to lower cognitive burden."Defiance
A
47

The GoF book explicitly ties itself to OOP - the title is Design Patterns - Elements of Reusable Object-Oriented Software (emphasis mine).

Araliaceous answered 24/11, 2009 at 15:49 Comment(0)
S
35

Design Patterns in Dynamic Programming by Peter Norvig has thoughtful coverage of this general theme, though about 'dynamic' languages instead of 'functional' (there's overlap).

Sponson answered 29/11, 2008 at 20:23 Comment(1)
It's worth that only four of those patterns are explicitly obviated by first class functions. First class types end up being the big eliminator of effort (eliminating six), but there's also an equally large number of patterns that are eliminated by specific features offered by the very nontraditional common lisp object system that substantively generalizes OOP and makes it a lot more powerfulArithmetician
S
27

Here's another link, discussing this topic: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

In his blog post Edward describes all 23 original GoF patterns in terms of Haskell.

Silassilastic answered 19/10, 2012 at 8:15 Comment(2)
The article doesn't seem to really show design patterns in Haskell, but show how Haskell addresses those needs without said patterns.Valerivaleria
@Fresheyball: Depends upon your definition of patterns. Is mapping a function over a list a variant of the Visitor pattern? I have generally thought the answer was "yes." Patterns are supposed to go beyond a particular syntax. The function being applied could be wrapped as an object or passed as a function pointer, but the concept is the same, to me. Do you disagree?Abducent
Q
24

When you try to look at this at the level of "design patterns" (in general) and "FP versus OOP", the answers you'll find will be murky at best.

Go a level deeper on both axes, though, and consider specific design patterns and specific language features and things become clearer.

So, for example, some specific patterns, like Visitor, Strategy, Command, and Observer definitely change or disappear when using a language with algebraic data types and pattern matching, closures, first class functions, etc. Some other patterns from the GoF book still 'stick around', though.

In general, I would say that, over time, specific patterns are being eliminated by new (or just rising-in-popularity) language features. This is the natural course of language design; as languages become more high-level, abstractions that could previously only be called out in a book using examples now become applications of a particular language feature or library.

(Aside: here's a recent blog I wrote, which has other links to more discussion on FP and design patterns.)

Quail answered 29/11, 2008 at 23:15 Comment(5)
How can you say the visitor pattern "disappears"? Doesn't it just turn from "make a Visitor interface with a bunch Visit methods" into "use union types and pattern matching"?Myriad
Yes, but that changed from a pattern which is a design idea that you read about in a book and apply to your code, to "just code". That is, "use union types and pattern matching" is just how you normally code stuff in such a language. (Analogy: if no languages had for loops and they all just had while loops, then "For" might be an iteration pattern. But when for is just a construct supported by the language and how people code normally, then it's not a pattern - you don't need a pattern, it's just code, man.)Quail
Put another way, a maybe-not-bad litmus test for "is it a pattern" is: present code written this way to a second-year undergraduate student majoring in CS with one year of experience programming in your language. If you show them the code, and they go "that's a clever design", then it's a pattern. If you show them the code, and they go "well, duh!", then it's not a pattern. (And if you show this "Visitor" to anyone who has done ML/F#/Haskell for a year, they will go "well, duh!")Quail
Brian: I think we just have different definitions of a "pattern". I consider any identifiable design abstraction to be a pattern, while you consider only non-obvious abstractions to be a pattern. Just because C# has foreach and Haskell has mapM doesn't mean that they don't have the Iterator pattern. I see no problem in saying that the Iterator pattern is implemented as the generic interface IEnumerable<T> in C# and the typeclass Traversable in Haskell.Myriad
It might be that non-obvious patterns are of use to software engineers but all patterns are of use to language designers. I.e. "If you're creating a new language, make sure to include a clean way to express the iterator pattern." Even the obvious patterns are of interest when we start asking the question, "Is there a better syntax for expressing this idea?" After all, that's what lead someone to create foreach.Abducent
U
18

I would say that when you have a language like Lisp with its support for macros, then you can build you own domain-specific abstractions, abstractions which often are much better than the general idiom solutions.

Unintelligent answered 16/12, 2008 at 19:44 Comment(4)
I'm completely lost. To up something with abstractions... What does that mean?Permeable
You can build domain specific abstractions (even embedded ones) without macros. Macros just let you pretty them up by adding custom syntax.Fewness
You can think of Lisp as a set of Legos for building programming languages - it's a language but it's also a metalanguage. Which means that for any problem domain, you can custom-design a language that doesn't have any obvious deficiencies. It will require some practice, and Kurt Gödel may disagree, but it's worth spending some time with Lisp to see what it brings to the table (hint, macros).Aeschylus
JD: That's a bit like saying that you can always write assembly. Macros can do pretty nontrivial code transformations. You can technically build and manipulate the AST of a DSL in other languages, but macros let you do that with normal expressions of the language you are using so that the DSL can be integrated a lot more cleanlyArithmetician
S
17

Norvig's presentation alludes to an analysis they did of all the GoF patterns, and they say that 16 of the 23 patterns had simpler implementations in functional languages, or were simply part of the language. So presumably at least seven of them either were a) equally complicated or b) not present in the language. Unfortunately for us, they are not enumerated!

I think it's clear that most of the "creational" or "structural" patterns in GoF are merely tricks to get the primitive type systems in Java or C++ to do what you want. But the rest are worthy of consideration no matter what language you program in.

One might be Prototype; while it is a fundamental notion of JavaScript, it has to be implemented from scratch in other languages.

One of my favorite patterns is the Null Object pattern: represent the absence of something as an object that does an appropriate kind of nothing. This may be easier to model in a functional language. However, the real achievement is the shift in perspective.

Streeto answered 30/11, 2008 at 7:11 Comment(7)
What an odd analysis to do since the GoF patterns were specifically designed for class-based OOP languages. Seems a bit like analyzing whether pipe wrenches are good for doing electrical work.Eam
@munificent: Not really. Object-orientation provides polymorphism; functional programming generally provides polymorphism.Viridescent
@Viridescent an OO programmer means something very different by polymorphism than a functional programmer.Mauchi
@Mauchi I disagree. The OO programmer may think they mean something different, but they don't.Viridescent
@Viridescent In my experience, an OO programmer is typically referring to subtype polymorphism (often just using Object), utilising casts to achieve it, or ad-hoc polymorphism (overloading etc). When a functional programmer says polymorphism they mean parametric polymorphism (i.e. works for any type of data - Int, function, list), which is perhaps more like OO's generic programming than it is like anything OO programmers usually call polymorphism.Mauchi
@Mauchi OK, yes. My claim is that all adequate OO systems offer at least a type of parametric polymorphism. I find it instructive that the introduction of templates into C++ has massively influenced style to the point where parametric polymorphism by templates has overtaken use of classes. It makes for a much nice language.Viridescent
In the Lisp object system (CLOS), you can specialize any method argument to the null class whose only instance is the familiar nil: Lisp's "empty list", "list terminator" and "false". null is a subclass of all classes implicitly (forming the bottom of the type "spindle"), and so a null class match, if available, is always most specific for an argument whose value is nil. And there goes your "null pattern". Null pattern for null minds.Glutinous
C
9

As others have said, there are patterns specific to functional programming. I think the issue of getting rid of design patterns is not so much a matter of switching to functional, but a matter of language features.

Take a look at how Scala does away with the "singleton pattern": you simply declare an object instead of a class. Another feature, pattern matching, helps avoiding the clunkiness of the visitor pattern. See the comparison here: Scala's Pattern Matching = Visitor Pattern on Steroids

And Scala, like F#, is a fusion of OO-functional. I don't know about F#, but it probably has these kind of features.

Closures are present in functional language, but they need not be restricted to them. They help with the delegator pattern.

One more observation. This piece of code implements a pattern: it's such a classic and it's so elemental that we don't usually think of it as a "pattern", but it sure is:

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

Imperative languages like Java and C# have adopted what is essentially a functional construct to deal with this: "foreach".

Communitarian answered 29/11, 2008 at 21:43 Comment(3)
I would say that Scala includes first-class support for the singleton pattern. The pattern is still there, but the boilerplate code needed to apply the pattern is greatly reduced compared to Java.Unavoidable
If opinions were like a*******, well... Look at the rest of the answers. "you simply declare an object instead of a class" is so true, I would explicitly call it an object literal though (ie var singleton = {};). I also like the mention about the foreach pattern. Unfortunately, it looks like most of the people who answered/commented on this question don't understand functional programming and would rather justify the usage of OOP design patterns. +1 for providing concrete examples, I would give more if I could.Shapiro
@Unavoidable I can't comment on Scala/Haskell but in JavaScript (ie. hybrid functional/imperative) there is absolutely no boilerplate you just adjust the way you declare objects by using combinations of object literal syntax, anonymous functions, passing functions as first class members, and allowing multiple inheritance (eliminating the need for interface contracts).Shapiro
T
9

And even the OO design pattern solutions are language specific.

Design patterns are solutions to common problems that your programming language doesn't solve for you. In Java, the Singleton pattern solves the one-of-something (simplified) problem.

In Scala, you have a top level construct called Object in addition to Class. It's lazily instantiated and there is only one.You don't have to use the Singleton pattern to get a Singleton. It's part of the language.

Thereby answered 30/11, 2008 at 18:2 Comment(0)
I
9

Patterns are ways of solving similar problems that get seen again and again, and then get described and documented. So no, FP is not going to replace patterns; however, FP might create new patterns, and make some current "best practices" patterns "obsolete".

Isabellisabella answered 26/8, 2013 at 2:10 Comment(5)
GoP patterns are ways of solving the problem of the limitations of a particular type of programming language getting in your way. For instance "I want to indirect on classes, and tell them to make objects" -> "You can't, but you can make metaclass-like objects called a Factory". "I want multiple dispatch" -> "You can't, but there is labyrinth you can implement called the Visitor Pattern". Etc. None of the patterns make sense if you're not in a OOP language with specific limitations.Glutinous
I don't know about "none" of them making sense in other languages, but I'll agree that a lot of them don't make sense in other languages. Adapter and Bridge seem to have more multilingual possibilities, decreasing a bit for visitor, and perhaps a bit less for listener. However, patterns across languages are always going to suffer from "how to do language X's operation in language Y" type shoring up of the language's natural boundaries. A perfect example was the Singleton pattern, which is basically, how do I get C globals in OOP? (which I'll answer, you shouldn't).Isabellisabella
I second Kaz: Pattern are not "way of solving similar problems that get seen again and again" but "way of solving similar problems that get seen again and again AND have to be rewritten again and again because the language doesn't allow one to write it only once". In other words, if the language allowed the pattern to be extracted/abstracted in library/class/module etc it stops being a pattern but becomes a library/class/module. In FP, it's much much easier to extract/abstract bit of codes to a function, therefore "pattern" are more easily converted in reusable code making them not a pattern.Featherston
Your interpretation is welcome, but the GoF book was clear to define a pattern, and if your read the introductory chapters, it says nothing about languages, or weaknesses of the language. Certainly some languages have areas which will make them leverage some patterns more often, but whether you write it ten times (cut and paste) or implement it once with ten realizations (sub-classing), or have a framework configured to do it ten slightly different ways, is only an implementation detail of the pattern being exposed.Isabellisabella
Wandering back into this conversation after years, I think that a lot of people associate Patterns with a specific programming language or a specific programming paradigm. They can be used in such a context, but they existed before programming. "A timeless way of building" discusses patters in building architecture and community planning. This implies that the pattern oriented techniques can be used outside of "limitations of a language" unless you want to call building construction a programming language :)Isabellisabella
G
8

The GoF Design Patterns is coding workaround recipes for OO languages that are descendants of Simula 67, like Java and C++.

Most of the "ills" treated by the design patterns are caused by:

  • statically typed classes, which specify objects, but are not themselves objects;
  • restriction to single dispatch (only the leftmost argument is used to select a method, the remaining arguments are considered as static types only: if they have dynamic types, it's up to the method to sort that out with ad-hoc approaches);
  • distinction between regular function calls and object-oriented function calls, meaning that object-oriented functions cannot be passed as functional arguments where regular functions are expected and vice versa; and
  • distinction between "base types" and "class types".

There isn't a single one of these design patterns that doesn't disappear in the Common Lisp Object System, even though the solution is structured in essentially the same way as in the corresponding design pattern. (Moreover, that object system precedes the GoF book by well over a decade. Common Lisp became an ANSI standard the same year that that book was first published.)

As far as functional programming is concerned, whether or not the patterns apply to it depends on whether the given functional programming language has some kind of object system, and whether it is modeled after the object systems which benefit from the patterns. That type of object-orientation does not mix well with functional programming, because the mutation of state is at the front and centre.

Construction and non-mutating access are compatible with functional programming, and so patterns which have to do with abstracting access or construction could be applicable: patterns like Factory, Facade, Proxy, Decorator, and Visitor.

On the other hand, the behavioral patterns like State and Strategy probably do not directly apply in functional OOP because mutation of state is at their core. This doesn't mean they don't apply; perhaps they somehow apply in combination with whatever tricks are available for simulating mutable state.

Glutinous answered 9/12, 2013 at 17:45 Comment(4)
"The GoF Design Patterns are coding workaround recipes" is simply a false statement.Trudytrue
Can you please expand on the 2nd ill ('restriction to single dispatch')? For example. singledispatch [docs.python.org/3/library/… in Python can trigger different handlers based on the type of the leftmost arg as you have said. Why is this an ill? It's useful for registering handlers and implementing the Command pattern. Are you referring to the need to have double dispatch in order to implement the visitor pattern?Angell
@LukePurnell You need double dispatch in order to map over some sequence or set, using an object as a function, where the method varies according to the element of the set/sequence and the function-object. Given some object o, for all elements e of this set S, call the method m(e, o). That's not the Visitor pattern: the visitor pattern is the recipe for producing the ugly boiler plate needed to do this simple thing C++ or Java and their ilk.Glutinous
@LukePurnell E.g. in Lisp, the ancient mapcar function which precedes the existence of object systems, can do visiting when the structure is a list of objects. E.g. (mapcar (lambda (elem) (desired-method some-object elem)) list-of-objects). The only boilerplate is the lambda which simulates partial application; it adapts the one-argument interface expected by mapcar to our requirement to select both an object and a method: we'd like to use the desired-method, and have its left argument be some-object. mapcar's definition is oblivious to the existence of an object system.Glutinous
N
7

I'd like to plug a couple of excellent but somewhat dense papers by Jeremy Gibbons: "Design patterns as higher-order datatype-generic programs" and "The essence of the Iterator pattern" (both available here: http://www.comlab.ox.ac.uk/jeremy.gibbons/publications/).

These both describe how idiomatic functional constructs cover the terrain that is covered by specific design patterns in other (object-oriented) settings.

Necropolis answered 17/9, 2010 at 0:30 Comment(0)
Z
6

You can't have this discussion without bringing up type systems.

The main features of functional programming include functions as first-class values, currying, immutable values, etc. It doesn't seem obvious to me that OO design patterns are approximating any of those features.

That's because these features don't address the same issues that OOP does... they are alternatives to imperative programming. The FP answer to OOP lies in the type systems of ML and Haskell... specifically sum types, abstract data types, ML modules, and Haskell typeclasses.

But of course there are still design patterns which are not solved by FP languages. What is the FP equivalent of a singleton? (Disregarding for a moment that singletons are generally a terrible pattern to use)

The first thing typeclasses do is eliminate the need for singletons.

You could go through the list of 23 and eliminate more, but I don't have time to right now.

Zoom answered 30/11, 2008 at 22:20 Comment(1)
How do typeclasses (the FP equivalent of OOP interfaces) eliminate the need for singletons (the FP equivalent of global state)?Myriad
T
5

I think only two GoF Design Patterns are designed to introduce the functional programming logic into natural OO language. I think about Strategy and Command. Some of the other GoF design patterns can be modified by functional programming to simplify the design and keep the purpose.

Tu answered 1/10, 2012 at 10:51 Comment(1)
Thing is, many patterns' main point is to harness polymorphism to do things that decent support for FP concepts could allow automatically. (Most incarnations i've seen of Builder, for example, are just half-assed currying.) Once you can easily treat functions as values, patterns often simplify to the point of triviality. They become "pass a callback" or "have a dictionary of callbacks" -- and different builder classes, for example, can all but disappear. IMO a pattern stops being a pattern once it's trivial enough to be just how things work, rather than something you need to implement.Denaedenarius
K
4

Functional programming does not replace design patterns. Design patterns can not be replaced.

Patterns simply exist; they emerged over time. The GoF book formalized some of them. If new patterns are coming to light as developers use functional programming languages that is exciting stuff, and perhaps there will be books written about them as well.

Kynthia answered 30/11, 2008 at 22:0 Comment(5)
Design patterns can not be replaced? That is a bit closed minded I think. We can probably all agree that design patterns are meant to solve programming problems, and I at least would like to hope that some day we can solve those problems without design patterns.Lohr
Any particular pattern might be replaceable, but the concept of patterns can not. Remember that the term "pattern" arose in field of architecture.Novikoff
Patterns are not meant to solve programming problems. Patterns are ways we program. The documentation of patterns are meant to help solve programming problems.Sandon
@Torbjørn: Patterns are ways we program when the language gets in the way. They exist because of some mismatch between the program's desired behavior and the language's built-in abilities, where the requirements and the abilities don't map well or map ambiguously. If it weren't for that, there'd be no pattern; you'd have one implementation that's just how things are done, and other implementations would effectively be not worth considering.Denaedenarius
Except that patterns truly exist only to facilitate communication. There is no other purpose. And in all of the design meetings I've attended over the years, a discussion of the algorithm is what was important, not the pattern. The pattern rarely explained what was truly going on in any meaningful sense. Does it explain precisely the O(n) vs O(n Log(n)) impacts? No. Does it explain how easily it'll fit with the existing architecture? No. Full scale algorithm discussions do. I'm not arguing that patterns should be retired per se, but if they were, almost nothing would suffer as a result.Deviationism
L
4

Essentially, yes!

  • When a pattern circumvents the missing features (high order functions, stream handling...) that ultimalty facilitate composition.
  • The need to re-write patterns' implementation again and again can itself be seen as a language smell.

Besides, this page (AreDesignPatternsMissingLanguageFeatures) provides a "pattern/feature" translation table and some nice discussions, if you are willing to dig.

Lecroy answered 19/10, 2012 at 19:44 Comment(0)
B
3

In the new 2013 book named "Functional Programming Patterns- in Scala and Clojure" the author Michael.B. Linn does a decent job comparing and providing replacements in many cases for the GoF patterns and also discusses the newer functional patterns like 'tail recursion', 'memoization', 'lazy sequence', etc.

This book is available on Amazon. I found it very informative and encouraging when coming from an OO background of a couple of decades.

Bookstall answered 20/5, 2014 at 12:4 Comment(0)
C
2

As the accepted answer said, OOP and FP all have their specific patterns.

However, there are some patterns which are so common that all programming platforms I can think of should have. Here is an (incomplete) list:

  • Adapter. I can hardly think of a useful programming platform which is so comprehensive (and self-fulfilled) that it does not need to talk to the world. If it is going to do so, an adapter is definitely needed.

  • Façade. Any programming platforms that can handle big source code should be able to modularise. If you were to create a module for other parts of the program, you will want to hide the "dirty" parts of the code and give it a nice interface.

  • Interpreter. In general, any program is just doing two things: parse input and print output. Mouse inputs need to be parsed, and window widgets need to be printed out. Therefore, having an embedded interpreter gives the program additional power to customise things.

Also, I noticed in a typical FP language, Haskell, there is something similar to GoF patterns, but with different names. In my opinion this suggest they were there because there are some common problems to solve in both FP and OOP languages.

  • Monad transformer and decorator. The former used to add additional ability into an existing monad, the latter add additional ability to an existing object.
Celestinacelestine answered 12/6, 2014 at 12:28 Comment(0)
C
2

OOP and the GoF patterns deal with states. OOP models reality to keep the code base as near as possible to the given requirements of reality. GoF design patterns are patterns that were identified to solve atomic real world problems. They handle the problem of state in a semantic way.

As in real functional programming no state exists, it does not make sense to apply the GoF patterns. There are not functional design patterns in the same way there are GoF design patterns. Every functional design pattern is artifical in contrast to reality as functions are constructs of math and not reality.

Functions lack the concept of time as they are always return the same value whatever the current time is unless time is part of the function parameters what makes it really hard to prrocess "future requests". Hybrid languages mix those concepts make the languages not real functional programming languages.

Functional languages are rising only because of one thing: the current natural restrictions of physics. Todays processors are limited in their speed of processing instructions due to physical laws. You see a stagnation in clock frequency but an expansion in processing cores. That's why parallelism of instructions becomes more and more important to increase speed of modern applications. As functional programming by definition has no state and therefore has no side effects it is safe to process functions safely in parallel.

GoF patterns are not obsolete. They are at least necessary to model the real world requirements. But if you use a functional programming language you have to transform them into their hybrid equivalents. Finally you have no chance to make only functional programs if you use persistence. For the hybrid elements of your program there remains the necessity to use GoF patterns. For any other element that is purely functional there is no necessity to use GoF patterns because there is no state.

Because the GoF patterns are not necessary for real functional programming, it doesn't mean that the SOLID principles should not be applied. The SOLID principles are beyond any language paradigm.

Cresting answered 28/6, 2016 at 22:11 Comment(1)
FP can have state -- just no global, shared, or mutable state.Boffin
H
1

I think that each paradigm serves a different purpose and as such cannot be compared in this way.

I have not heard that the GoF design patterns are applicable to every language. I have heard that they are applicable to all OOP languages. If you use functional programming then the domain of problems that you solve is different from OO languages.

I wouldn't use functional language to write a user interface, but one of the OO languages like C# or Java would make this job easier. If I were writing a functional language then I wouldn't consider using OO design patterns.

Haletta answered 29/11, 2008 at 20:18 Comment(0)
H
1

OOP and FP have different goals. OOP aims to encapsulate the complexities/moving parts of software components and FP aims to minimize the complexity and dependencies of software components.

However these two paradigms are not necessarily 100% contradicting and could be applied together to get the benefit from both worlds.

Even with a language that does not natively support functional programming like C#, you could write functional code if you understand the FP principles. Likewise you could apply OOP principles using F# if you understand OOP principles, patterns, and best practices. You would make the right choice based on the situation and problem that you try to solve, regardless of the programming language you use.

Hosey answered 13/12, 2016 at 10:57 Comment(0)
R
1

It does, in that a high-level functional PL (like OCaml, with classes, modules, etc.) certainly supersedes OOP imperative languages in type versatility and power of expression. The abstractions do not leak, you can express most of your ideas directly in the program. Therefore, yes, it does replace design patterns, most of which are ridiculously simplistic compared to functional patterns anyhow.

Romansh answered 23/5, 2020 at 23:45 Comment(0)
A
0

In functional programming, design patterns have a different meaning. In fact, most of OOP design patterns are unnecessary in functional programming because of the higher level of abstraction and HOFs used as building blocks.

The principle of an HOF means that functions can be passed as arguments to other functions. and functions can return values.

Annates answered 5/9, 2018 at 5:33 Comment(0)
T
0

The paramount characteristic of functional programming, IMHO, is that you are programming with nothing but expressions -- expressions within expressions within expressions that all evaluate to the last, final expression that "warms the machine when evaluated".

The paramount characteristic of object-oriented programming, IMHO is that you are programming with objects that have internal state. You cannot have internal state in pure functions -- object-oriented programming languages need statements to make things happen. (There are no statements in functional programming.)

You are comparing apples to oranges. The patterns of object-oriented programming do not apply to function programming, because functional programming is programming with expressions, and object-oriented programming is programming with internal state.

Trochelminth answered 26/3, 2020 at 10:7 Comment(1)
Hmm, I should have noticed that the question was eleven years old before answering. :-)Trochelminth
D
0

Brace yourselves.

It will aggravate many to hear me claim to have replaced design patterns and debunked SOLID and DRY. I'm nobody. Nevertheless, I correctly modeled collaborative (manufacturing) architecture and published the rules for building processes online along with the code and science behind it at my website http://www.powersemantics.com/.

My argument is that design patterns attempt to achieve what manufacturing calls "mass customization", a process form in which every step can be reshaped, recomposed and extended. You might think of such processes as uncompiled scripts. I'm not going to repeat my (online) argument here. In short, my mass customization architecture replaces design patterns by achieving that flexibility without any of the messy semantics. I was surprised my model worked so well, but the way programmers write code simply doesn't hold a candle to how manufacturing organizes collaborative work.

  • Manufacturing = each step interacts with one product
  • OOP = each step interacts with itself and other modules, passing the product around from point to point like useless office workers

This architecture never needs refactoring. There are also rules concerning centralization and distribution which affect complexity. But to answer your question, functional programming is another set of processing semantics, not an architecture for mass custom processes where 1) the source routing exists as a (script) document which the wielder can rewrite before firing and 2) modules can be easily and dynamically added or removed.

We could say OOP is the "hardcoded process" paradigm and that design patterns are ways to avoid that paradigm. But that's what mass customization is all about. Design patterns embody dynamic processes as messy hardcode. There's just no point. The fact that F# allows passing functions as a parameter means functional and OOP languages alike attempt to accomplish mass customization itself.

How confusing is that to the reader, hardcode which represents script? Not at all if you think your compiler's consumers pay for such features, but to me such features are semantic waste. They are pointless, because the point of mass customization is to make processes themselves dynamic, not just dynamic to the programmer wielding Visual Studio.

Dextrorotation answered 7/4, 2020 at 18:57 Comment(0)
O
0

Some patterns are easier to implement in a language supporting FP. For example, Strategy can be implemented using nicely using closures. However depending on context, you may prefer to implement Strategy using a class-based approach, say where the strategies themselves are quite complicated and/or share structure that you want to model using Template Method.

In my experience developing in multi-paradigm languages (Scala, Ruby), the FP implementation works well in simple cases, but where the context is more complicated the OOP based approach is a better fit.

The FP approach does not replace the OOP approach, it complements it.

Oho answered 26/4, 2020 at 15:14 Comment(0)
E
0

Let give an example of the wrong premise you state.

The adapter pattern we have in OOP as usecase adapter such as in cleanarch and ddd can be implemented in Functional via the monad variation of Option.

You are not replacing them but transforming them.

Electrolytic answered 4/7, 2022 at 11:29 Comment(0)
B
0

I would like to give a concrete example. In fact, I failed an Amazon review because the person interviewing me did not understand functional programming and kept quoting back the so-called open-closed principle found in OOP designs.

Let's take a language like F# that can do both OOP and functional language, and let's take a problem that is very commonly used in examples. The problem is: represent various shapes like circles, squares, rectangles, etc. as a type and then define calculations, such as their area.


OOP solution

let pi = System.Math.PI

type IShape =
    abstract member Area: unit -> float
    abstract member Perimeter: unit -> float

type Circle(radius) =
    interface IShape with
        member this.Area() = pi * radius * radius
        member this.Perimeter() = 2.0 * pi * radius

type Rectangle(length, width) =
    interface IShape with
        member this.Area() = length * width
        member this.Perimeter() = 2.0 * length + 2.0 * width

type Square(side) =
    interface IShape with
        member this.Area() = side * side
        member this.Perimeter() = 4.0 * side

type EquilateralTriangle(side) =
    interface IShape with
        member this.Area() = sqrt(3.0) * side * side / 4.0
        member this.Perimeter() = 3.0 * side

Functional solution

let pi = System.Math.PI

type Shape =
    | Circle of radius: float
    | Rectangle of length: float * width: float
    | Square of side: float
    | EquilateralTriangle of side: float

let area shape =
    match shape with
    | Circle radius -> 2.0 * pi * radius
    | Rectangle (length, width) -> length * width
    | Square side -> side * side
    | EquilateralTriangle side -> sqrt(3.0) * side * side / 4.0

let perimeter shape =
    match shape with
    | Circle radius -> pi * radius * radius
    | Rectangle (length, width) -> 2.0 * length + 2.0 * width
    | Square side -> 4.0 * side
    | EquilateralTriangle side -> 3.0 * side

Discussion

Now, F# is by far one of the most concise OOP languages, but even then, the functional version is shorter and more concise and direct. There's no extraneous information. It's just a type and then functions. This is called type-driven development.

What is interesting is that the OOP and functional versions are sort of "transposes" of one another. The OOP version first arranges a hierarchy of types and then assigns functions to concrete versions in that hierarchy. The functional version creates a single type and then assigns a function for each behavior that that type might encounter. To me, this is much more clear and direct: you define a type and then you define behaviors on that type. In OOP, the behaviors are less clear because they are spread across a hierarchy and implementations within that hierarchy.

What happened in my interview is almost the exact same thing we see here. The OOP person could not get their head around the fact that I was "breaking" the open-closed principle. They asked, if you add another shape then you have to go into all of your functions. Yes, that's actually the point! It's not a detriment to the functional style, because I want my program to tell me exactly where I should go change things when I make edits, and exhaustive pattern matching makes that doable. That principle simply doesn't apply to functional programming because functional programming more directly exposes the types, which is what makes it so easy to structurally pattern match on them.

So in general, no, OOP design patterns do not belong in functional languages. However, the caveat is that for languages like F#, Racket, etc. that are multiparadigm, you use the right patterns for the right paradigm that you are using.

Bashful answered 1/4 at 20:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.