Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement the Observer Design Pattern in a pure functional way?

Let's say I want to implement an event bus using a OO programming language. I could do this (pseudocode):

class EventBus      listeners = []      public register(listener):         listeners.add(listener)      public unregister(listener):         listeners.remove(listener)      public fireEvent(event):         for (listener in listeners):             listener.on(event) 

This is actually the the observer pattern, but used for event-driven control flow of an application.

How would you implement this pattern using a functional programming language (such as one of the lisp flavors)?

I ask this because if one doesn't use objects, one would still need some kind of state to maintain a collection of all the listeners. More over, since the listeners collection changes over time, it would not be possible to create a pure functional solution, right?

like image 870
ivo Avatar asked Aug 05 '11 09:08

ivo


People also ask

What are implementation considerations of Observer pattern?

Implementation. Observer pattern uses three actor classes. Subject, Observer and Client. Subject is an object having methods to attach and detach observers to a client object.

Where is Observer pattern used in real life?

A real world example of observer pattern can be any social media platform such as Facebook or twitter. When a person updates his status – all his followers gets the notification. A follower can follow or unfollow another person at any point of time.

What is the purpose of observer design pattern?

The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

How does the Observer pattern implement the Open Closed Principle?

The observer pattern allows for the Open Closed principle. This principle states that a class should be open for extensions without the need to change the class. Using the observer pattern a subject can register an unlimited number of observers.


2 Answers

Some remarks on this:

I am not sure how it is done, but there is something called "functional reactive programming" which is available as a library for many functional languages. This is actually more or less the observer pattern done right.

Also the observer pattern is usually used for notifying changes in state, as in the various MVC implementations. However in a functional language there is no direct way to do state-changes, unless you use some tricks such as monads to simulate the state. However if you simulate the state changes using monads you will also get points where you can add the observer mechanism inside the monad.

Judging from the code you posted it seems that you are actually doing event driven programming. So the observer pattern is a typical way to get event driven programming in Object oriented languages. So you have a goal (event driven programming) and a tool in the object oriented world (observer pattern). If you want to use the full power of functional programming you should check what other methods are available for achieving this goal instead of directly porting the tool from the object oriented world (it might not be the best choice for a functional language). Just check what other tools are available here and you will probably find something that fits your goals much better.

like image 182
2 revs, 2 users 75% Avatar answered Sep 21 '22 03:09

2 revs, 2 users 75%


If the Observer pattern is essentially about publishers and subscribers then Clojure has a couple of functions that you could use:

  • add-watch
  • remove-watch

The add-watch function takes three arguments: a reference, a watch function key, and a watch function that is called when the reference changes state.

Clearly, because of the changes in mutable state, this is not purely functional (as you clearly requested), but add-watcher will give you a way to react to events, if that's the effect you were seeking, like so:

(def number-cats (ref 3))  (defn updated-cat-count [k r o n]   ;; Takes a function key, reference, old value and new value   (println (str "Number of cats was " o))   (println (str "Number of cats is now " n)))  (add-watch number-cats :cat-count-watcher updated-cat-count)  (dosync (alter number-cats inc)) 

Output:

Number of cats was 3 Number of cats is now 4 4 
like image 34
Scott Avatar answered Sep 22 '22 03:09

Scott