Hi,

the first part of your code runs fine - the problem should be the some cast of the result fo "foo". As an example

1
2
3
4
5
6
7
8
9
10
11
 

    let foo (typ : System.Type) =
        let e = System.Activator.CreateInstance(typ)
        let pub = (e :?> Event<'a>).Publish
        pub

    let t = typeof<Event<System.EventArgs>>

    let x = (foo t) : IEvent<System.Eve

works fine with me.

But after all it should be no issue to make the messages inside your MailboxProcessor generic - you just have to carry the generic-types with you, whereever you use it (same as in C# or any other CLR language).

Maybe you can post the hole story?

By on 11/29/2010 9:45 PM ()

I always try to pare down my questions to just the parts that seem important. But it just never seems to work out when I'm asking questions here of F#. I guess I'm just still too new at this.

Its kinda frustrating because I feel like I'm so close to getting this to work, but keep running in to issues at the last minute. When I originally posted the question I was trying to make do by passing along System.Type with the MailboxProcessor message. But as I said I couldn't cast back to IEvent after object creation.

My messages looked like this:

1
2
3
4
5
6
7
type EventAggregatorMessage =
    | GetPublish of Type * AsyncReplyChannel<IEvent<obj>>
    | GetEvent of Type * AsyncReplyChannel<Event<obj>>
    | GetEventCount of Type * AsyncReplyChannel<int>
    | TryGetEvent of Type * AsyncReplyChannel<Option<Event<obj>>>
    | RemoveEvent of Type
    | TriggerEvent of Type

So I went back again and made the MailboxProcessor messages generic, and tried passing along the type as an Unchecked.defaultof<'E>, but that leads to the incredibly unhelpful error mesasge complaining about 'E not being generalized because it would escape its scope, what the heck does that mean?

Anyways here's the whole code for the EventAggregator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
let dbgprtfn = printfn

type EventAggregatorMessage<'E> =
    | GetPublish of 'E * AsyncReplyChannel<IEvent<'E>>
    | GetEvent of 'E * AsyncReplyChannel<Event<'E>>
    | GetEventCount of 'E * AsyncReplyChannel<int>
    | TryGetEvent of 'E * AsyncReplyChannel<Option<Event<'E>>>
    | RemoveEvent of 'E
    | TriggerEvent of 'E

type TypeComparable(typ : Type) =
    let typ = typ

    interface IComparable with
        member x.CompareTo (o: obj) =
            let lt = (o :?> TypeComparable)
            dbgprtfn "comparing %s to %s" (typ.ToString()) (lt.Type.ToString())
            typ.ToString().CompareTo(lt.Type.ToString())

    member x.Type 
        with get() = typ
    member x.CompareTo o =
        (x :> IComparable).CompareTo o
    override x.Equals o =
        typ.ToString().Equals(o.GetType.ToString())
    override x.GetHashCode() =
        typ.ToString().GetHashCode()

type EventAggregatorState<'E> =
    { EventMap : Map<TypeComparable, Event<'E> * IEvent<'E>> }

let GetEventF<'E> map =
    let tc = new TypeComparable(typeof<Event<'E>>)

    let res = Map.tryFind tc map
    match res with 
    | Some e -> 
        dbgprtfn "found event"
        e, map
    | None   ->
        dbgprtfn "adding event"
        let newE = Activator.CreateInstance(tc.Type)
        newE, Map.add tc newE map

type EventAggregator() =
    let mbox = MailboxProcessor.Start(fun mbox ->
        let rec loop(state) = async {
            let! msg = mbox.Receive()
            match msg with
            | GetEvent(e, chnl) ->
                let tc = new TypeComparable(typeof<Event<'E>>)
                let result = Map.tryFind tc state.EventMap
                match result with
                | Some evt -> 
                    dbgprtfn "Found event"
                    chnl.Reply(fst evt)
                    return! loop(state)
                | None   -> 
                    dbgprtfn "Adding new event"
                    let newE = Activator.CreateInstance<Event<'E>>()
                    let pub = newE.Publish
                    chnl.Reply(newE)
                    return! loop({state with 
                                    EventMap = Map.add tc (newE, pub) state.EventMap})
            | GetPublish(t, chnl) ->
                let tc = new TypeComparable(typeof<Event<'E>>)
                let result = Map.tryFind tc state.EventMap
                match result with
                | Some pub ->
                    chnl.Reply(snd pub)
                    return! loop(state)
                | None     ->
                    let evt = Activator.CreateInstance<Event<'E>>()
                    let pub = evt.Publish
                    chnl.Reply(pub)
                    return! loop({state with 
                                    EventMap = Map.add tc (evt, pub) state.EventMap})
            | GetEventCount(e, chnl) ->
                chnl.Reply(state.EventMap.Count)
                return! loop(state)
            | TryGetEvent(e, chnl) ->
                let tc = new TypeComparable(typeof<Event<'E>>)
                let result = Map.tryFind tc state.EventMap
                match result with
                | None -> 
                    chnl.Reply(None)
                | Some e -> 
                    chnl.Reply(Some (fst e))
                return! loop(state)
            | RemoveEvent(e) ->
                let tc = new TypeComparable(typeof<Event<'E>>)
                return! loop({state with EventMap = Map.remove tc state.EventMap})
            | TriggerEvent(payload) ->
                let tc = new TypeComparable(typeof<Event<'E>>)
                let evt = Map.tryFind tc state.EventMap
                match evt with
                | Some e -> (fst e).Trigger payload
                | _      -> ()
                return! loop(state)
        }

        loop({EventMap = Map.empty}))

    member x.Subscribe<'E> f =                   // <- code is not sufficiently generic ?????
        let t = Unchecked.defaultof<Event<'E>>
        let evt = mbox.PostAndReply(fun chnl -> GetPublish(t, chnl)) 
        evt.Subscribe f
    member x.GetPublishedEvent<'E>() =
        let t = Unchecked.defaultof<Event<'E>>
        mbox.PostAndReply(fun chnl -> GetPublish(t, chnl))
    member x.Trigger<'E> payload =
        mbox.Post(TriggerEvent(payload))
    member x.GetEvent<'E>() =
        mbox.PostAndReply(fun chnl -> GetEvent(Unchecked.defaultof<Event<'E>>, chnl))
    member x.TryGetEvent<'E>() =
        mbox.PostAndReply(fun chnl -> TryGetEvent(Unchecked.defaultof<Event<'E>>, chnl))
    member x.RemoveEvent<'E>() =
        mbox.Post(RemoveEvent(Unchecked.defaultof<Event<'E>>))
    member x.Count 
        with get() = mbox.PostAndReply(GetEventCount(Unchecked.defaultof<Event<'E>>))


let evInt = eventAggregator.GetPublishedEvent<int>()
let v = ref 0
let callback = (fun i -> v := i)
let disposable = evInt |> Observable.subscribe callback  // <- type mismatch error
        
eventAggregator.GetEvent<int>().Trigger 42
disposable.Dispose
By on 11/29/2010 10:49 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper