[Haskell-cafe] Observer pattern in Haskell?

Tom Lokhorst tom at lokhorst.eu
Mon Nov 9 18:08:45 EST 2009


I haven't looked at the code extensively, I just wanted to comment on
this point:

> There is no way to remove an observer, which is something I'd expect to have
> available.  I realise this would require assigning a key to each observer
> (and thus perhaps storing them in an associative map) or some way to filter
> them, but I think if you can only ever add observers, it will get awkward.

It might be convenient to have the `addObserver` function return a
"detachment function" as its result.

At Microsoft, Erik Meijer is working on an "Rx framework" [1], which
is an implementation of the GoF Observer pattern. The implementation
is, as Erik describes it, the mathematical dual of the Enumerable
pattern.

In the Rx framework, the `addObserver` method returns an `IDisposable`
object with a single method `dispose()`, this will detach the observer
from its subject. This is more convenient than trying to remember the
unique object identity of the observer (as that disallows anonymous
lambdas).

I don't know how this would work in a Haskell context, but it might be
interesting to think about.


- Tom Lokhorst

[1]: http://channel9.msdn.com/shows/Going+Deep/Expert-to-Expert-Brian-Beckman-and-Erik-Meijer-Inside-the-NET-Reactive-Framework-Rx/


On Mon, Nov 9, 2009 at 3:50 PM, Neil Brown <nccb2 at kent.ac.uk> wrote:
> Andy Gimblett wrote:
>>
>> Hi all,
>>
>> I've been doing some GUI programming recently, using wx.  To help manage
>> dependencies between state and UI elements, I looked for a Haskell version
>> of the Observer design pattern, and I found an implementation written by
>> Bastiaan Heeren of ou.nl [1].
>>
>> Now, before I make a hackage release:
>>
>> 1. Does anyone have any comments, on either version?
>
> There is no way to remove an observer, which is something I'd expect to have
> available.  I realise this would require assigning a key to each observer
> (and thus perhaps storing them in an associative map) or some way to filter
> them, but I think if you can only ever add observers, it will get awkward.
>
>> 2. In particular, is the MVar version sensible?  I'm aiming for mutual
>> exclusion between threads.  I _think_ I've got it, but I'm perhaps not
>> familiar enough with the semantics of MVar to be certain.  Advice
>> appreciated.  If it _is_ sensible, then is there any reason not to just use
>> this, and discard the IORef version?
>
> It looks fine (and thread-safe) to me, but I'd agree that you may as well
> just use the MVar version and leave out the IORef version.
>
>> The current implementation is synchronous, in that any observer functions
>> are called immediately and synchronously (and in the same thread as the
>> change of subject value).  I'm pondering extending the package with an
>> asynchronous version where the update just trips a flag, and the observer
>> function picks this up later - possibly in another thread.  The idea there
>> is to help in cases where certain operations have to be in a particular
>> thread.  But this will mean a change to the typeclass too, I guess - or the
>> addition of another one for observers themselves.  Again, any thoughts?
>
> I was a bit surprised at first that the observers were called synchronously.
>  Asynchronous is what I'd expect, and it's also harder to code the
> asynchronous handlers wrongly.  One blocking call (such as putMVar) in a
> synchronous handler can screw up your whole program by delaying the
> subsequent observers (and at that stage, the order in which the observers
> were added begins to matter).  But my idea of how asynchronous would be
> implemented seems different to yours, judging by your description.  Why not
> just augment this function in the synchronous version:
>
> notifyObservers :: Subject sub val => sub -> IO ()
> notifyObservers subject =
>  do value <- getValue subject
>     observers <- getObservers subject
>     mapM_ ($ value) observers to become:
>
> notifyObserversAsync :: Subject sub val => sub -> IO ()
> notifyObserversAsync subject =
>  do value <- getValue subject
>     observers <- getObservers subject
>     mapM_ (forkIO . ($ value)) observers
>
> This is what I was expecting to happen -- all the observer actions are
> spawned off into their own thread to run whatever code they want (either
> communicating back to an existing thread, or performing some long in-depth
> action).
>
> Thanks,
>
> Neil.
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>


More information about the Haskell-Cafe mailing list