views:

12905

answers:

14

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. 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 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 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.

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?

+35  A: 

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, 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.

S.Lott
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.
Juliet
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.
Adam Byrtek
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?
PhiLho
MVC isn't OO design. It's architectural design -- that pattern applies pretty widely.
S.Lott
@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.
S.Lott
A: 

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.

Vincent Ramdhanie
+12  A: 

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).

Darius Bacon
+2  A: 

As others have said, there are patterns specific of 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: http://andymaleh.blogspot.com/2008/04/scalas-pattern-matching-visitor-pattern.html

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

Closures are present in functional language, but 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".

Germán
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.
JacquesB
+132  A: 

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's a particularly controversial viewpoint 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.

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 a 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.

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.

Edit: 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. ;)

jalf
"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 langua" It's not controversial. It's false. Design Patterns describe solutions to problems. Nothing to do with language shortcomings in any way.
S.Lott
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.
Justice
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 language
jalf
Wow...! This was a good answer! :)
Thomas Hansen
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.
Alexandru Nedelcu
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.
Thedric Walker
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.
jalf
Could you elaborate more on the "singletons are generally a terrible pattern to use"? I'm really curious, no flame intended.
zarzych
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.
JacquesB
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?
jalf
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.
jalf
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?
Rubendv
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).
Robert Harvey
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.
elviejo
"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.
Tony Morris
S. Lott. Here are a couple of interesting riffs on the theme that design patterns are about language flaws:http://c2.com/cgi/wiki?AreDesignPatternsMissingLanguageFeatureshttp://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.
andybak
@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.
jalf
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.
Tony Morris
@Tony: could you explain, instead of simply saying I'm wrong? How do pure FP languages allow mutable state and side effects then?
jalf
@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 http://projects.tmorris.net/public/what-does-monad-mean/artifacts/1.1/chunk-html/ar01s02s02s02.html
Tony Morris
@jalf: Second, other languages like Clean use a technique called uniqueness typing, which is different altogether to the IO type constructor.
Tony Morris
@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."
Tony Morris
@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.
Tony Morris
@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.
Tony Morris
@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.
Tony Morris
@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.
Tony Morris
@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.
CodexArcanum
Hello CodexArcanum, No.
Tony Morris
@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.
CodexArcanum
@Tony Morris Don't suppose you have a slideshow on Arrows too? ;)
CodexArcanum
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.
Tony Morris
+4  A: 

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.)

Brian
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"?
Gabe
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.)
Brian
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!")
Brian
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.
Gabe
+3  A: 

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.

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.
munificent
+3  A: 

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.

+2  A: 

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.

ktingle
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.
Metropolis
+4  A: 

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, 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.

jganetsk
How do typeclasses (the FP equivalent of OOP interfaces) eliminate the need for singletons (the FP equivalent of global state)?
Gabe
+6  A: 

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.

Anders Rune Jensen
I'm completely lost. To up something with abstractions... What does that mean?
tuinstoel
fixed thanks! :)
Anders Rune Jensen
+8  A: 

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. 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 idiom 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.

Thank you! *this* is what patterns are about—"conceptual chunking" to lower cognitive burden.
Randall Schulz
+6  A: 

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

bright
+1  A: 

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/).

sclv