11

In the past few years, the languages I like to use are becoming more and more "functional". I now use languages that are a sort of "hybrid": C#, F#, Scala. I like to design my application using classes that correspond to the domain objects, and use functional features where this makes coding easier, more coincise and safer (especially when operating on collections or when passing functions).

However the two worlds "clash" when coming to design patterns. The specific example I faced recently is the Observer pattern. I want a producer to notify some other code (the "consumers/observers", say a DB storage, a logger, and so on) when an item is created or changed.

I initially did it "functionally" like this:

producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed

But I'm now wondering if I should use a more "OO" approach:

interface IItemObserver {
  onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...

producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop

Which are the pro and con of the two approach? I once heard a FP guru say that design patterns are there only because of the limitations of the language, and that's why there are so few in functional languages. Maybe this could be an example of it?

EDIT: In my particular scenario I don't need it, but.. how would you implement removal and addition of "observers" in the functional way? (I.e. how would you implement all the functionalities in the pattern?) Just passing a new function, for example?

8
  • What about Actors?
    – kiritsuku
    Commented Mar 29, 2012 at 8:42
  • Forget about the classes and all that OOPish useless stuff. You'd better think in terms of modules instead (see SML and OCaml languages for an inspiration).
    – SK-logic
    Commented Mar 29, 2012 at 9:14
  • @Antoras If you could a comparison with an approach based on Actors, it would be more then welcome :) Commented Mar 29, 2012 at 9:22
  • 2
    @dema80, OCaml is perfectly multi-paradigm. Modules are not related to functional programming at all. There is an advanced module system in a purely imperative Ada, for example. And all the fame OOP gained should actually go to modules - all the good in OOP is just various forms of simulating the modules functionality. You can forget totally all that classes and use their syntax for expressing modules instead, thinking in terms of modules, not OOP. Btw., that's exactly what Microsoft did with their mscorlib - not much OOP there, just modules and namespaces.
    – SK-logic
    Commented Mar 29, 2012 at 10:12
  • 1
    I think the better question is "Is there any clarity or organization you lose by doing it the FP way?"
    – djechlin
    Commented Feb 5, 2014 at 13:07

4 Answers 4

0

That's a good example of two different approaches that carry the notion of performing a task outside the boundary of concern for the calling object.

While it's clear in this example that you should go for the functional approach, in general it will really depend on how complex the behavior the called object has to exhibit is. If it really is a matter of complex behavior, where you'll find yourself reapplying similar logic often, and function generators cannot be used to clearly express it, then you'll probably want to go for class composition or inheritance, where you'll have a bit more freedom to reuse and extend existing behavior on an ad-hoc basis.

One pattern I did observe, though, is that usually developers go for the functional approach initially and only once the demand for more granular behavior arises they decide to go for a class-based approach. I know, for instance, Django went from function-based views, template loaders and test runners to class-based ones once the benefits and requirements became obvious, but not before that.

4
  • I think this answer is a little bit misguided. Functional programming is not programming (only with) functions. Functional programming uses abstractions as well, so there's no dichotomy here.
    – Doval
    Commented Feb 5, 2014 at 12:29
  • "composition or inheritance, where you'll have a bit more freedom to reuse and extend existing behavior" you're speaking like this ISN'T one of the core benefits of pure FP. modularity, composability and reuseability just happen naturally when you code functionally, this isn't an "OOP thing"
    – sara
    Commented Jul 17, 2016 at 19:04
  • @FilipDupanović I was refering to reusing and extending existing code. pure functions are probably the most composable things in all of programming. you're wrote as if you can't manage complexity in a pure functional environment. managing complexity by composing simple parts into larger but still simple and opaque parts is the whole core of FP, and many would argue it is even way better at it than OOP. I don't agree with the dichotomy between "functional one-liners that don't scale VS solid OOP that scales no problem" you put forward.
    – sara
    Commented Jul 18, 2016 at 9:25
  • you said that FP was inferior when it comes to composing and reusing code, and that when applications grow more complex, OOP will more or less always be "better". I claim this is simply false and misleading, and so I thought it warranted a downvote. Is that really "purist zealotry"? I won't discuss this further here though since comments aren't for extended discussions like these.
    – sara
    Commented Jul 18, 2016 at 12:30
5

The functional version is much shorter, easier to maintain, easier to read, and generally, vastly superior in just about every respect imaginable.

Many- although, far from all- patterns are to make up for the lack of features in OOP, such as Observers. Those are far better modelled functionally.

2
  • I agree and have the same feeling. Commented Mar 29, 2012 at 9:39
  • I agree and have the same feeling.But I was sort of "cheating" with my functional code: in my case is all what I need, but what if I need to add and remove "observers"? I edited my question Commented Mar 29, 2012 at 9:41
5

Your "FP guru" is partially right; many OO patterns are hacks to do functional things. (His claim that this is the reason there's few FP languages seems dubious at best.) The Observer and Strategy patterns are trying to emulate first-class functions. The Visitor Pattern is a hack to simulate pattern matching. Your IItemObserver is just a function in disguise. Pretending it's different from any other function that takes an item doesn't buy you anything.

Objects are just one kind of data abstraction. This paper will help shed some light on that topic. Objects can be useful, but it's important to recognize they're not appropriate for everything. There is no dichotomy; simply a matter of choosing the right tool for the right job, and functional programming in no way requires that you give up objects. That aside, functional programming is more than just using functions. It's also about minimizing side effects and mutation.

1
  • +1 for (IMO) a good answer. Also, thanks for the link to the paper: I had read it some time ago and then lost it. Now I have found it again.
    – Giorgio
    Commented Aug 24, 2014 at 18:20
-1

I can't really answer the question because I'm not good at functional languages; but I think you should be less concerned about approach as long as what you have works. From what I understand , as long as you are not adding more listeners in the future or not changing the listeners during execution, you can very well skip the Observer pattern here.

I disagree that design patterns make up for "limitations" in OO languages. They are there to make good use of OOP features. Polymorphism and inheritance are features, not limitations. Design patterns make use of these features to promote flexible design. You can make an absolutely non-OO program in an OO. You can, with a lot of care, write an entire program containing objects which have no state, mimicking FP.

16
  • 1
    "You can, with a lot of care, write an entire program containing objects which have no state, mimicking FP.", of course, and through discipline you can do the same in imperative languages too. :) In general, I do not think at design patters as something to make up for limitations, but consider the case of the Visitor pattern.. Commented Mar 29, 2012 at 9:06
  • Also, I am not concerned with the code: however, I would like to confront with fellow programmers on the approach (to my understanding, this is what programmers.se is for, otherwise I would have posted my Q on SO) Commented Mar 29, 2012 at 9:07
  • Every time you're spotting a repeating pattern, it is nothing but an indication of a severe limitation of your language and modelling framework. There shall be no patterns at all in an ideal world.
    – SK-logic
    Commented Mar 29, 2012 at 9:13
  • @SK-logic: Unfortunately the world isn't ideal, and the real world has patterns. Which is why OO languages have inheritance , to implement repeateable code without having to rewrite them all over again.Real world object have a state, thats why there is a State pattern
    – DPD
    Commented Mar 29, 2012 at 9:23
  • 1
    @SK-logic: I'm well aware that some languages are programmable, and some permit the enforcing of effects from the type system too. My point is that, like "OOP", "pattern" is a sadly misunderstood term. A pattern is something that keeps reappearing in similar but different forms, that does not admit a uniform solution. Sometimes you can make those quasirepetitions disappear - multimethods make Visitor/double dispatch redundant. That does not imply "all patterns are signs of a deficiency", because that means the real world is deficient. Patterns originated in architecture, not software. Commented Mar 29, 2012 at 11:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.