Phil Trelford's Array
POKE 36879, 255

What’s happening? Loosely coupled events

April 13, 2010 01:37 by phil

So the Reactive Extensions (Rx) demonstrate some sweet syntax for handling CLR events. How about doing the same thing for loosely coupled events exposed through an Event Broker like Prism’s IEventAggregator. For example if you wanted to publish an event from any window that could be observed by any other window without them all having intimate knowledge of each other.

The following is a simple F# event broker coded on the train this morning (Note: the interface ISubscribe<’a> could be replaced by IObservable<’a>):

type ITrigger<'a> =
    abstract member Trigger : 'a -> unit
    
type ISubscribe<'a> =     
    abstract member Subscribe : ('a -> unit) -> unit 

type IHappen<'a> = 
    inherit ITrigger<'a> 
    inherit ISubscribe<'a>
            
type IHappenings =
    abstract member ObtainHappening<'a> : unit -> IHappen<'a>
        
type Happenings () =
    let happenings = System.Collections.Generic.Dictionary<System.Type,_>()
    interface IHappenings with
        member this.ObtainHappening<'a> () =
            let CreateHappening () = 
                let subscribers = ref []
                { new IHappen<'a> with                        
                    member this.Subscribe f = 
                        lock(this) (fun _ -> 
                            subscribers := f::!subscribers)
                    member this.Trigger (x) =                      
                        !subscribers |> List.iter (fun f -> f x)                       
                }
            lock (this) (fun _ ->    
                match happenings.TryGetValue(typeof<'a>) with
                | true, happen -> unbox happen
                | false, _ ->                 
                    let happen = CreateHappening ()
                    happenings.Add(typeof<'a>,box happen)
                    happen
            )                    
    end

 

Then, in Starbucks for a coffee and some higher-order functions :

module Happening =   
    open System
    open System.Windows.Threading

    let Subscribe<'a> f (happening:ISubscribe<'a>) =         
        happening.Subscribe f
    let OnDispatcher<'a> (happening:ISubscribe<'a>) =
        let dispatcher = Dispatcher.CurrentDispatcher
        { new ISubscribe<'a> with  
            member this.Subscribe f = 
                happening.Subscribe (fun x -> 
                dispatcher.Invoke(Action(fun _ -> f x)) |> ignore
            )            
        }        
    let Filter<'a> filterF (happening:ISubscribe<'a>) =
        { new ISubscribe<'a> with  
            member this.Subscribe f = 
                happening.Subscribe (fun x -> if filterF x then f x)          
        }
    let Map<'a,'b> mapF (happening:ISubscribe<'a>) =        
        { new ISubscribe<'b> with  
            member this.Subscribe f = 
                happening.Subscribe (fun x -> let y = mapF x in f y)            
        }
    let Delay<'a> milliseconds (happening:ISubscribe<'a>) =
        { new ISubscribe<'a> with  
            member this.Subscribe f = 
                happening.Subscribe (fun x -> 
                    async { 
                        do! Async.Sleep(milliseconds) 
                        f x 
                    } |> Async.Start )            
        }

 

Finally, here is Matthew Podwysocki’s cool Time Flies Like An Arrow sample rewritten to use more loosely-coupled events:

module Test =  
    open System
    open System.Windows
    open System.Windows.Controls
    open System.Windows.Input
    open System.Windows.Media
 
    let getPosition (element : #UIElement) (args : MouseEventArgs) =
        let point = args.GetPosition(element)
        (point.X, point.Y)    
     
    type TimeFliesWindow(happenings:IHappenings) as this =
        inherit Window()

        do this.Title <- "Time files like an arrow"

        let canvas = 
           Canvas(Width=800.0, Height=400.0, Background = Brushes.White) 
        do this.Content <- canvas

        let happen = happenings.ObtainHappening<MouseEventArgs>()
        do this.MouseMove         
            |> Observable.subscribe happen.Trigger |> ignore

        do "F# can react to first class events!"
            |> Seq.iteri(fun i c ->  
                let s = TextBlock(Width=20.0, 
                                Height=30.0, 
                                FontSize=20.0, 
                                Text=string c, 
                                Foreground=Brushes.Black, 
                                Background=Brushes.White)
                canvas.Children.Add(s) |> ignore              
                happen                  
                |> Happening.Map (getPosition canvas)           
                |> Happening.Delay (i * 100)
                |> Happening.OnDispatcher                            
                |> Happening.Subscribe (fun (x, y) ->                                                        
                     Canvas.SetTop(s, y) 
                     Canvas.SetLeft(s, x + float ( i * 10)))              
           )        
              
    let happenings = new Happenings()
    let win = TimeFliesWindow(happenings)
    [<STAThread>]
    do (new Application()).Run(win) |> ignore

Tags:
Categories: F# | .Net | Architecture
Actions: E-mail | Permalink | Comments (5) | Comment RSSRSS comment feed

Comments

April 13. 2010 05:18

pingback

Pingback from alvinashcraft.com

Dew Drop – April 13, 2010 | Alvin Ashcraft's Morning Dew

alvinashcraft.com

April 14. 2010 01:13

trackback

Implementing IObservable and extending Observable

Implementing IObservable and extending Observable

Phillip Trelford's blog... evidently

April 14. 2010 02:09

pingback

Pingback from blog.cwa.me.uk

The Morning Brew - Chris Alcock  » The Morning Brew #579

blog.cwa.me.uk

April 14. 2010 09:20

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        What’s happening? Loosely coupled events
        [trelford.com]
        on Topsy.com

topsy.com

April 16. 2010 11:16

trackback

F# Discoveries This Week 04/16/2010

It’s finally here.  After years of work, F# 2.0 is out and ready to change the way you program in

Rick Minerich's Development Wonderland

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading