[Haskell-cafe] Observer pattern in Haskell?

Neil Brown nccb2 at kent.ac.uk
Mon Nov 9 09:50:43 EST 2009


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.


More information about the Haskell-Cafe mailing list