It's very common for people to repeat that claim that Peter Norvig gave in that presentation that design patterns are work arounds for language shortcoming. This is only true for some patterns. MVC, Facade, Composite and others are obviously applicable in most paradigms. Moreover, some patterns are obviated by library improvements, such as Singleton.
The truth is that modern OOP (even in Java) doesn't many of the patterns in the GoF book any longer. That's not much of a surprise though: GoF is 18 years old. Approaches to problems have changed and the patterns used are a bit different.
Idiomatic Haskell is full of design patterns. Here's a few of the popular ones: monads, composition (of functions, not objects), maybe. When I stopped use Haskell actively, they were just getting a handle on the overuse of monads. Pattern overuse had occurred, just as in Java. It seems that pattern popularity follows the Hype Cycle (http://en.wikipedia.org/wiki/Hype_cycle)
Composite is often a work around for poor support for delegation in the language; so too is Facade to some extent. The language feature that will make MVC obsolete is less obvious, but I have confidence that one will arise.
>The truth is that modern OOP (even in Java) doesn't many of the patterns in the GoF book any longer.
Oh how I wish I shared your optimism. You still need Visitors and Factories and Singletons in Java, because the language still doesn't provide the features that replace them. At least with java 8 we'll finally be getting lambda.
Well, a Facade is very much about hiding away something that you have no control over. Even in an FP environment, you need to communicate with third parties, and they generally to a crap job of their api's.
It is, but most patterns are like that to a certain extent. I'm not thinking of a language that eliminates MVC so much as one where MVC is so easy and natural that it's not even a concept that needs a name. (Just like passing a function as a parameter doesn't really replace the Command pattern, it just means it's so normal and obvious that you don't think of it as a pattern).
I'm not sure this discussion applies to MVC as easily as you're making out. It's a design pattern, not a code pattern. Let's make it more general, though. How would a language that "gets rid" of the problem of modular code look like? That is, a language that makes it natural to organize your code based on high-level functionality, responsibility, and/or dependency? It seems to me that such a language would have to bring in arbitrary limits and introduce a lot more problems than it would solve, assuming it's feasible at all. I'm not saying this is something I've given a lot of thought to, it just has a "concept smell" to me.
There are cross-cutting concerns in software development that don't have an obvious technical solution. Modular code is one of them. There's no logical reason for us to organize code in such a roundabout way, it's only to help us measly humans reason about it more efficiently during the process of software development. So, there's unlikely to be a way to unproblematically translate that process into something logical.
A lot of the reason for MVC is to avoid mixing business logic and data. So maybe one route to making it redundant is to have the language make more of a distinction between data objects and logic objects. Scala's case classes give you a slight nudge in this direction - after a while you become uneasy at seeing a complex function on a case class, or a non-case class with lots of data members - but it's quite subtle; maybe a future language could make it harder to do these two things.
I think there's this constant tension in language design between giving you the power to do really cool things (python) and the constraints to stop you screwing up (java). But I think that more modern languages are getting further down the funnel, combining the advantages of both.
Rails scaffolding provides a language (the rails comma done apps) that sets up MVC. Rails itself has code that follows the pattern. The code you write in your app doesn't follow the pattern, it fits into it.
I don't think you can refer to function composition as a design pattern. The syntax for function composition in Haskell is so light that most of the time it's more effort to write in all the arguments explicitly than it is to use composition. Consider
lineLengths = map (length . words) . lines
as compared to
lineLengths text = map length (map words (lines text))
I don't think that monads are a design pattern either (in this I agree with Tony Morris [1]). Monads are a unified method of doing contextual computations. They are far too general to be a design pattern in themselves (how many design patterns do you know that encompass failure, error handling, nondeterminism, probabilistic computation, logging, backtracking search, mutable state, asynchronous computation, continuations, I/O...)
Monads should be more accurately thought of as an interface that is implemented by many types (in Haskell terminology, a class that many types are instances of).
I am not saying that there aren't any design patterns in Haskell - just that function composition and monads are not examples of design patterns. Instead, common design patterns include
* Writing tail-recursive, accumulating helper functions where you'd use a for loop in an imperative language:
sum xs = go 0 xs
where
go result [] = result
go result (x:xs) = go (result + x) xs
* Using strict left folds to accumulate values
sum = foldl' (+) 0
* Using runStateT and its cousins to tame stateful computations
sum xs = execStateT (go xs) 0
where
go [] = return ()
go (x:xs) = modify (+x) >> go xs
* Using a record of functions to encapsulate related functions that might be given different implementations (like a lightweight version of classes)
data Lens a b = Lens { get :: a -> b, set :: a -> b -> a }
* Using libraries like Conduit or Pipes to structure your program as a flow of data through componentized parts.
Then there are 'large scale' design patterns, like
* Structuring your code in three parts: (i) data type declarations, (ii) pure functions operating on the data, (iii) a thin I/O wrapper to glue the pure functions together.
* Using the MonadIO, MonadState, MonadWriter etc classes to keep your code implementation agnostic.
To re-iterate: I agree with you that functional languages (like Haskell) still have design patterns. But I don't think that monads or function composition are good examples of functional design patterns.
>Here's a few of the popular ones: monads, composition (of functions, not objects), maybe
None of those are design patterns in haskell. They may well be design patterns in other functional languages (probably only monads really), but in haskell they are very basic, fundamental features of the language. That is like saying classes and variables are design patterns.
No, it's a lot like saying that composition of objects (using a basic feature called "properties") is a design pattern - which it is: GoF call it Composite. The idea that design patterns have to be large and possibly overwrought elements is untrue. Composing functions _is_ a design pattern. Consider how it makes parsec different to bison.
You are just deliberately misrepresenting what they called the composite pattern to make it sound simpler than it is. Nobody said anything about things being large or overwrought, that is you bringing your bias to the discussion. Just proclaiming "function composition is a design pattern" is useless, support your claim. Parsec being different from yacc doesn't in any way provide support for the notion that function composition is a design pattern. In a language where function composition is possible, but isn't available directly in the language, then function composition could be a design pattern. And the pattern would be describing how to implement the composition operator. Given that I do not need to implement it, it is clearly not a design pattern.
> Given that I do not need to implement it, it is clearly not a design pattern.
We have a definitional disagreement: this is a bad definition. Here's what I think, straight off page 3 of GoF:
> A design pattern names, abstracts and identifies the key aspects of a common design structure that make it useful for creating a reusable [object-oriented] design.
Composite is a great counter example to your definition of a design pattern, because it is an example of an important pattern (IMO the most important in GoF) in which no special implementation is required.
PS. Calm down and stop accusing people on the internet of bad faith
The definition you quoted matches the one I gave. And as I already said, composite does require an actual design pattern. It isn't just "use properties", I think you might want to re-read the book.
P.S. If you want people to think you are making a good faith attempt at discussion, making passive aggressive douche notes like this is counter productive.
GOF patterns are indeed language agnostic, as long as you work within the OO paradigm.
You can indeed apply the same pattern (fe visitor) in Python, Java, C++, Go. This is also the reason why it isn't that difficult to move from one OO language to another.
Functional programming languages fall within another paradigm. You do have patterns like 'fold', 'map', or 'nexus' but in general, they tend to be very small. This is because functions allow both more strict and more flexible composition than objects. Again, once you know Lisp, or Scheme or ML you can jump from one language to another, although from Scheme to Haskell will be a larger jump (purity & laziness) than from Scheme to Erlang.
Two final remarks:
) The declarative paradigm (Prolog, Mercury, ...) seems to be out of flavour these days.
) Every self respecting developer should have mastered at least 1 language in every paradigm.
The take-away point being "If a pattern is really valuable, then it should be part of the core language". Perl, of course, has very strong functional features, as described in Higher Order Perl:
>>"If a pattern is really valuable, then it should be part of the core language"
I think this is what has caused so many misunderstandings people have about Perl over time. Perl has so many of such patterns built which solve many problems with default. In fact most people fall in love with Perl for these reasons. To understand why that is important you need to recognize such patterns in your problems, if you don't see any you will feel the solutions contained in a language as rubbish.
The entire Unix command line toolset is entirely about this thing. Unless you learn how to reduce a problem to a text processing problem, you will never learn to appreciate it.
The visitor pattern is a way around a lack of multiple dispatch: choosing a method based on the run-time type of all arguments. Common Lisp, for example, has no need of the visitor pattern since it has multiple dispatch.
The O.P. is about when the design patterns are needed, not what you need to implement them.
Incidentally, the visitor pattern doesn't require OO for implementation, it requires a type system and single dispatch, i.e. choosing a method based on the run-time type of its first argument. Python doesn't have single dispatch. So the visitor pattern doesn't really work in Python, you essentially have to code a huge number of branches, e.g. n switch statements, each with n cases, in order to dispatch n^2 ways.
Wait, what? Python doesn't have single dispatch? How does it pick which block of code to run when you say a.f(x)? That dispatches on the class of a doesn't it?
You're correct. Python objects have methods that explicitly take "self" as the first argument. In Java and C#, self or "this" is implied. They all are single-dispatch languages unless an add-on library is used.
This is actually a pattern that I've been looking for a name for, for years! And you've actually sold the OP's featured answer for me by making me use that word ("pattern"), notice the concept's genuine applicability to this context, and realize it's not a Java-spawned vulgarity.
About logic programming: I think the current feeling is that its great for certain kinds of subproblems cropping up in a program but not so hot for structuring the whole thing, and that therefore Prolog or something like it is better as a library than a language. There are nice logic programming libraries for any Lisp (core.logic in Clojure is particularly nice, I'd say), for Haskell (some having only the search part of Prolog, without logical variables, others having both).
GoF DP have failed to deliver their premises. They are not language agnostic. They are useful for education and to help communication when speaking about code. They have never helped me to develop code. Sometimes, my code contains a DP or another one, but it is never the most stable parts of my architectures.
Did you read the book? The original patterns were highly relevant at the time. The immortality of DP is the the notion that we should identify and name and share patterns as we invent new ones for changing times.
Did K&R fail to deliver because not everyone writes C for everything today?
Yes, I bought it and learned it. At that time (1999) I was preparing an interview where questions about this kind of knowledge were expected. I was already an experienced developer, the presentation of the DP was new for me and the promises were exciting. But in fact, I had already encountered most of the patterns in projects that were not great examples of good coding (IMHO).
Outside this interview (I succeeded), knowing DP has never helped me to be a better developer.
The patterns in the GoF are often encountered in libraries written in object oriented languages that do not use much templates or parallelism. There were many in Java library. I think I have not found any of them in the C++ standard library.
Now, I see singleton used everywhere because people are too lazy to design correctly program startup. When tasking is involved, incorrect use of singleton causes very nasty bugs (a crash every month).
For me, singleton is a dangerous pattern (almost as bad as global variables, sometime worse), the others patterns just happen to be the best way to do some common things. Identifying them is never a problem, having a name for them is useful.
The main proof of GoF failure is that we have never seen all the new books with new DPs. I still wait for a new list of common and language agnostic design patterns. Until that, DP remain for me a good example of failure.
C has exceeded all its expectations, what is the point of your comparison ?
If monads are design patterns, then why not rings (Num typeclass), which allow to use addition and multiplication in different contexts? If function composition is a design pattern, then why not function application? If chaining with '>>=' is a design pattern, then is using a semicolon a pattern too? If 'fold', 'map' are patterns, then why not 'sin' and 'cos' are? What is the difference? There's a "pattern" that after sine you put a number; there is also a "pattern" that after a fold you put a function. Thinking that higher-order functions are special is contrary to the spirit of functional programming. Functions and numbers are the same class of citizens, and a function taking a function should not be treated differently from one that takes a number.
Bare names of often-used library functions or language constructs should not be considered patterns, unless they occur as parts of repeating snippets. In this case, you should refactor or encapulate. If you cannot, this is a language flaw. It sucks if you have to type "x = new T(); try { ... } finally { delete x; }" each time. If your language allows to write "using (x = new T()) { ... }" then the pattern is eliminated.
Perhaps there are some "design patterns" in FP, but examples I find often are very weak. One thing that I would count as a pattern is "lift . lift . lift" used in Haskell in monad transformers.
The "lift . lift . lift" expression in multiply stacked monad transformers can often be completely eliminated by using the classes MonadReader, MonadState, MonadIO etc etc, with
putStrLn :: MonadIO m => String -> m ()
ask :: MonadReader r m => m r
modify :: MonadState s m => (s -> s) -> m ()
Now you can define
instance (MonadTrans t, MonadState s m) => MonadState s (t m) where ...
so that any monad transformer applied to a state monad is automatically another state monad. Win!
I agree with you in spirit, but I think your argument for why design patterns aren't present in functional languages can also be used to argue that there are no design patterns in object oriented languages, just by replacing the word "function" with the word "object". Witness:
Thinking that objects are special is contrary to the spirit of object-oriented programming. Objects and numbers are the same class of citizens, and a method taking an object should not be treated differently from one that takes a number.
Would you also argue that there are no (or very few) design patterns in object oriented languages?
I was trying to say that using higher-order functions (or functions taking objects) is not automatically a design pattern; a pattern must consist of a larger group of code. Patterns show up where abstractions provided by language are not powerful enough to capture analogies.
My take on this was along similar lines. To me, the presence of a "design pattern" means the language you're using isn't powerful enough to just encode the pattern in a reusable way; i.e., a design pattern is what you get when your language won't let you just write a library function.
It's a bit interesting that Norvig talks (some) about design patterns in Lisp, since the whole point of Lisp, and particularly Lisp macros, is to be able to rewrite the language itself, so if you find yourself wanting to use a design pattern, you just write the macro that implements it for you.
I spent so long at university writing out GoF style stuff (I want to say crap) like the visitor pattern in java. Now I write ruby. Those are hours and days I will never get back. I remember thinking at the time, "Why am I doing this? Surely this is what computers are for?"
I guess it's all education in the end. At least now I appreciate the value of the tools I have.
Not further than Clojure's multimethods. They dispatch on the value of an arbitrary function you write. This can be anything; it's not even restricted to types. Couple this with Clojure's ad hoc hierarchy system and even Haskell's types seem limited by comparison.
that means that the visitor pattern can be replaced with multiple dispatch (true) but neither map nor fmap imply that (and in fact many languages have the latter but not the former)
Clojure has both map and multimethods. It's trivial to map over a collection and dispatch to a different implementation for every element in the collection. With multiple collections, this extends to multiple dispatch over the corresponding elements in each collection.
Double-dispatch seems to be equivalent to Haskell's pattern matching.
Since Haskell does not have sub-typing, everything has the same runtime type. However, you can easily combine different types into a single algebraic data type and then dispatch by pattern matching against that type.
This probably isn't exactly the same as double-dispatch in other languages, but it seems to be very similar and to serve the same purpose.
Implementation details are the least valuable part design patterns. Implementing a visitor via map doesn't some how make it not a visitor. You still have to have some way of referring to what is going on when you do visitor like things.
If you read the replies you can see one from @TonyAlbrecht [1] that states it was implemented, and it was pretty good at the time.
That said all the programs that used design patterns always seemed too synthetic for me, as if the main purpose of the programs were generality. I learned that often it is better to keep other goals in mind, like practicality, simplicity or maintainability.
Had to giggle a little over this silly discussion of FP and are there design patterns or not. A strategy is the same intent whether you use a enclosed function or an anonymous object as the strategy. Remember the old computer programmer adage. Objects are the poor man's closure, and closures are the poor man objects.
But you may have run across monads. What are they, if not a design pattern for "dealing with global state"?
This to me belies that the author doesn't actually do much programming with monads. They're more like the core abstraction of an interpreter or a sequencing algebra than a design pattern for global state.
Considering functions s -> (a, s) as "global state" is closer to a "design pattern", though.
Jokes aside, there are monads for function application, for software transactional memory, for prolog's backtracking, for call/cc, for quantum computations and for running BASIC programs. And many more
I pretty often find myself "functionalizing" design patterns in in Java to split out the verbose part from the part that is interesting.
For instance, say you have a Log object that has 15 or so Log methods for different severity levels and signatures and you just want to write something that prepends something to anything that gets logged.
If you split this to "something that applies a function to any message passed to the log" and "a function that transforms a message" you get something that's certainly better if you implement more than one transform and probably better in the case of just one because the repetitive boilerplate is physically separated from the "business end".
The truth is that modern OOP (even in Java) doesn't many of the patterns in the GoF book any longer. That's not much of a surprise though: GoF is 18 years old. Approaches to problems have changed and the patterns used are a bit different.
Idiomatic Haskell is full of design patterns. Here's a few of the popular ones: monads, composition (of functions, not objects), maybe. When I stopped use Haskell actively, they were just getting a handle on the overuse of monads. Pattern overuse had occurred, just as in Java. It seems that pattern popularity follows the Hype Cycle (http://en.wikipedia.org/wiki/Hype_cycle)