7 ms·
Does Functional Programming Replace GoF Design Patterns?
- hendzen 14y agoThere's definitely a few design patterns that aren't applicable in functional languages. For example, the Visitor pattern can be naturally replaced with map and fmap in Haskell, which are far more powerful.
- scscsc 14y agoNegative, the Visitor pattern cannot be naturally replaced with map and fmap. Read up on double dispatch at e.g.: http://en.wikipedia.org/wiki/Double_dispatch http://en.wikipedia.org/wiki/Double_dispatch
- TeMPOraL 14y agoBut then neither of those problems exist in Common Lisp, as you have both first-order functions and multiple-dispatch in object model :).
- chongli 14y agoMaybe not in Haskell, but Clojure's multimethods handle this just fine.
- riffraff 14y agothat 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)
- chongli 14y agoClojure 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.
- riffraff 14y agoI will rephrase myself: the original statement is you can do V with F to which the reply was no, you can't do V with F and the subsequent reply was but you can do V with M and I pointed out that F does not imply V. Your current reply is Clojure has both F and M and can do V which is obvious, since M implies V already, but still unrelated to what F can do.
- Peaker 14y agoHaskell's type classes generalize further than multi methods do. You get dispatch based on any part of the type signature, not just the arguments.
- chongli 14y agoNot 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.
- Peaker 14y agoCan you implement the Read class in Clojure? class Read r where read :: String -> r (Let's ignore parse errors for our purposes here) Haskell supports return-type polymorphism. Clojure, being dynamically typed, cannot in general support this kind of overloading.
- chongli 14y agoGood point, though Clojure's extensible reader takes care of most of the use for this. I think it's a trade-off though. Can Haskell dispatch on a value rather than a type?
- Peaker 14y agoNote that this feature is far from being useful only for class Read. The Monad class actually depends on it too: class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a The type of "return" can only be expressed with return-type polymorphism. This means you cannot really implement Haskell-like monads with dynamic typing. You have to implement them in a less general way. As for dispatching on runtime values, that is ordinary pattern matching: f (RuntimeValue1 x y) = ... case 1 ... f (RuntimeValue2 x y) = ... case 2 ...
- 14y ago
- tikhonj 14y agoDouble-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.
- lelf 14y agoMany kinds of visitor pattern are just fmap (better call it Functor). Here is the good explanation: http://www.haskell.org/pipermail/haskell-cafe/2009-June/062352.html http://www.haskell.org/pipermail/haskell-cafe/2009-June/0623... Haskell people are good at math
- JonnieCache 14y agoI 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.
- lmm 14y agoNot map; pattern-matching is what replaces Visitor.
- Peaker 14y agoVisitor was about multiple dispatch, which multimethods, type-classes and pattern-matching can all handle. However, it was also about iterating an entire data structure (in the GoF example, a document hierarchy). This creates confusion as some emphasize the former (multimethods! pattern matching!) and some emphasize the latter (fmap! map!).
- stonemetal 14y agoImplementation 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.
- toolslive 14y agoGOF 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.
- fuzzix 14y ago"GOF patterns are indeed language agnostic" I enjoyed this series of articles published some years ago making the case that many of the useful design patterns are built into Perl already: http://www.perl.com/pub/2003/06/13/design1.html http://www.perl.com/pub/2003/06/13/design1.html http://www.perl.com/pub/2003/08/07/design2.html http://www.perl.com/pub/2003/08/07/design2.html http://www.perl.com/pub/2003/08/15/design3.html http://www.perl.com/pub/2003/08/15/design3.html 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: http://hop.perl.plover.com/ http://hop.perl.plover.com/
- kamaal 14y ago>>"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.
- dschiptsov 14y agohttp://norvig.com/design-patterns/ http://norvig.com/design-patterns/ The short answer is - no Java - no problem.) The longer answer is: Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of CommonLisp. http://c2.com/cgi/wiki?GreenspunsTenthRuleOfProgramming http://c2.com/cgi/wiki?GreenspunsTenthRuleOfProgramming
- wtetzner 14y ago>Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of CommonLisp. "Including Common Lisp." - Robert Morris http://paulgraham.com/quotes.html http://paulgraham.com/quotes.html
- pdonis 14y agoMy 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.
- pella 14y agostackoverflow: "Functional design patterns" http://stackoverflow.com/questions/11339637/functional-design-patterns http://stackoverflow.com/questions/11339637/functional-desig...
- calpaterson 14y agoIt'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 http://en.wikipedia.org/wiki/Hype_cycle)
- lmm 14y agoComposite 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.
- jrajav 14y agoIsn't MVC a specific case of "make your code modular"? I don't have as much confidence that I can imagine a language getting rid of that.
- lmm 14y agoIt 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).
- raverbashing 14y agoAs John Carmack said on twitter the other day: "Finally reading Design Patterns. The cross platform WYSIWYG editor used as an example doesn't "smell" like something actually implemented." https://twitter.com/ID_AA_Carmack/status/257683318907215873 https://twitter.com/ID_AA_Carmack/status/257683318907215873 I had the same impression. How to trust the authors with such unbelievable example.
- agazso 14y agoIf 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. [1] https://twitter.com/TonyAlbrecht/status/257685852153581569 https://twitter.com/TonyAlbrecht/status/257685852153581569
- sold 14y agoI have some objections. 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.
- crntaylor 14y agoI 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?
- sold 14y agoI 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.
- PaulHoule 14y agoI 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".
- chubbard 14y agoFor a hot minute this post was right above this post. http://doctrina.org/Javascript-Function-Invocation-Patterns.html http://doctrina.org/Javascript-Function-Invocation-Patterns.... 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.
- gaustin 14y agoWhat is more interesting to me is the way that the SOLID principles are expressed in functional languages. They are all still relevant.
- tel 14y agoBut 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.
- lelf 14y agoBut you may have run across monads. What are they, if not a design pattern for "dealing with global state"? No, monad is a monoid in the category of endofunctors © 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 Can you stop saying “design patterns” please?