You might find this post helpful.

[link:blogs.msdn.com]

1
let (doDrawScene,drawScene) = IEvent.create()

Hope this helps.

Can.

By on 2/29/2008 2:19 AM ()

If you want it to appears an event to other .NET languages then you need to create a property of Type "IHandlerEvent":
[link:research.microsoft.com]

There's a function create_EventHandler to do this documented here:
[link:research.microsoft.com]

By on 2/29/2008 2:22 AM ()

I still can't get this stuff to actually work. I'm trying to implement an XNA interface that requires four .NET events. The idiomatic code is just one line for each event but I can't see a direct equivalent in F#. Instead, I've managed to write this mess that compiles and implements the required interface but just raises exceptions instead of handling the events:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#light
#I @"C:\Program Files\Microsoft XNA\XNA Game Studio Express\v1.0\References\Windows\x86"
#r "Microsoft.Xna.Framework.dll"
#r "Microsoft.Xna.Framework.Game.dll"
open System
open System.Windows.Forms
open Microsoft.Xna.Framework
type GraphicsDeviceService(control : Control) =
	let mutable device : Graphics.GraphicsDevice = null
	let _, deviceDisposing = IEvent.create_HandlerEvent()
	let _, deviceReset = IEvent.create_HandlerEvent()
	let _, deviceResetting = IEvent.create_HandlerEvent()
	let _, deviceCreated = IEvent.create_HandlerEvent()
	interface Graphics.IGraphicsDeviceService with
		member this.GraphicsDevice
			with get() = device
		member this.remove_DeviceDisposing(f) = raise Not_found
		member this.add_DeviceDisposing(f) = raise Not_found
		member this.remove_DeviceReset(f) = raise Not_found
		member this.add_DeviceReset(f) = raise Not_found
		member this.remove_DeviceResetting(f) = raise Not_found
		member this.add_DeviceResetting(f) = raise Not_found
		member this.remove_DeviceCreated(f) = raise Not_found
		member this.add_DeviceCreated(f) = raise Not_found

How can I write that more succinctly in F#?

By on 2/29/2008 7:20 AM ()

Actually the samples we have given are for creating your own events.

For using with existing events you need to us add the function.

Here is a sample from the directx sample (in F# distribution)

1
2
3
4
5
6
7
8
9
10
let MouseTracker (c : #Control) = 
    let fire,event = IEvent.create() 
    let lastArgs = ref None
    c.MouseDown.Add(fun args -> lastArgs := Some args)
    c.MouseUp.Add  (fun args -> lastArgs := None)
    c.MouseMove.Add(fun args -> 
        match !lastArgs with
        | Some last -> fire(last,args); lastArgs := Some args
        | None -> ());
    event

IEvent is used to create your own event. the Add method of the events is used to assign the event. You might want to hold the reference if you intend to remove the events though.

Hope this helps.

By on 2/29/2008 7:37 AM ()

But IEvent.create() creates an F#-only event that is not compatible with other .NET languages. In order to create a standard .NET event (which I must do in this case because I am trying to implement an interface from XNA that requires a standard .NET event) then I am supposed to use IEvent.create_HandlerEvent(). However, I can only find minimal documentation about this function and no working examples so I've no idea how I'm supposed to use that function. I've tried all of the obvious solutions and none of them work.
The nearest I can find is this link to a previous post by Robert but he is just laboriously reimplementing each call by hand as I did. I'm hoping F# can do something at least as good as C# does here...
Cheers,
Jon.

By on 2/29/2008 9:04 AM ()

Hi Jon,
unfortunately I don't think there is an elegant solution. It is possible to implement class that exposes standard .NET/C# event, but implementing interface with event requires writing all the add_/remove_ functions manually.

Just a few examples how to use the functions mentioned earlier:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Creates standard .NET handler of type Handler<'a>, whic comes from the fslib.
// This is the best option for exposing C# compatible events, unless you need 
// specific type of event (eg. .NET System.EventHandler). In C# the declaration is:
//   event Handler<string> OnSomething;
type FsFoo1() = 
  let invokeSth, delegateSth = IEvent.create_HandlerEvent<FsFoo1,string>()
  member x.OnSomething = delegateSth

// If you need (for some reason) to create F# code that will generate 
// C#-compatible event of specific delegate type, then you can use 
// following (though this is probably not recomended as the first option with
// Handler<'a> is usually fine):
//    event EventHandler OnSomething;
type FsFoo2() = 
  let invokeSth, delegateSth = IEvent.create_DelegateEvent<EventHandler>()
  member x.OnSomething = delegateSth

If I needed to implement C# interface with event in F# then I would use the following:

1
2
3
4
// C# interface
interface IFoo {
  event EventHandler OnSomething; 
}
1
2
3
4
5
6
// F# implementation
type FsFoo() = 
  let invokeSth, delegateSth = IEvent.create_DelegateEvent<EventHandler>()
  interface IFoo with
    member x.add_OnSomething(h) = delegateSth.AddHandler h
    member x.remove_OnSomething(h) = delegateSth.RemoveHandler h

It isn't perfect, but I think it is the best that can be done currently. This looks like an incompleteness in the language though, so I hope it will be supported directly in the future.

By on 2/29/2008 1:01 PM ()

Hi all,

Yes, this is currently longer in F# than C# (well, some things have to be :-) ). The situation is somewhat analogous to immutable properties in C#, which currently take several lines to implement, but are relatively short in F#. We're aware of this and have recently been looking at a design addition which will make declaring events a little easier. However it is a surprisingly difficult addition to implement: while events would not become first-class declarations in the language they would effectively have to be treated as such by the compiler.

Luckily implementing abstract/interface events is very rare. While it is a little painful to do (as indicated above), it should at least work. If the code doesn't run correctly then please let us know. If you find yourself implementing hundreds of abstract events please do post the example.

Kind regards

don

P.S. Note that control design often takes a considerable amount of code, e.g. see the control in Chapter 11 of Expert F#. One problem is that boilerplate code like declaring events and reactive properties can't always be fully abstracted using functional techniques.

By on 3/1/2008 12:13 AM ()

I don't mind F# taking more code than C# to do something as unusual as this but I still can't actually get it to work. I already tried the approach that Tomas uses and it just leads to type errors. Presumably I must do some work to convert between the (function) types involved but wrapping them will presumably mean that I can no longer rely upon event removal to work so I'm basically going to have to implement my own event system. That clearly sucks in comparison to the one-liner in C#.

Incidentally, is it possible to compile C# code and F# code into the same DLL?

By on 3/1/2008 2:00 AM ()

I'll take a closer look at your original XNA sample - I believe we can adjust this.

Kind regards

don

By on 3/1/2008 2:15 AM ()

Hi Jon,

Try this code. It is very much as you had with the implementations filled in.

Should you get errors from create_DelegateEvent we can implement an adjusted version of that function - its definition is in the F# library but doesn't use any private constructs.

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
 

#nowarn "57"

#light

#I @"C:\Program Files\Microsoft XNA\XNA Game Studio\v2.0\References\Windows\x86"

#r "Microsoft.Xna.Framework.dll"

#r "Microsoft.Xna.Framework.Game.dll"

open System

open System.Windows.Forms

open Microsoft.Xna.Framework

type GraphicsDeviceService(control : Control) =

    let mutable device : Graphics.GraphicsDevice = null

    let _, deviceDisposing = IEvent.create_DelegateEvent()
    let _, deviceReset = IEvent.create_DelegateEvent()
    let _, deviceResetting = IEvent.create_DelegateEvent()
    let _, deviceCreated = IEvent.create_DelegateEvent()

    interface Graphics.IGraphicsDeviceService with

        member this.GraphicsDevice

            with get() = device

        member this.remove_DeviceDisposing(f) = deviceDisposing.RemoveHandler(f)
        member this.add_DeviceDisposing(f)    = deviceDisposing.AddHandler(f)

        member this.remove_DeviceReset(f) = deviceReset.RemoveHandler(f)
        member this.add_DeviceReset(f) = deviceReset.AddHandler(f)

        member this.remove_DeviceResetting(f) = deviceResetting.RemoveHandler(f)
        member this.add_DeviceResetting(f) = deviceResetting.AddHandler(f)

        member this.remove_DeviceCreated(f) = deviceCreated.RemoveHandler(f)
        member this.add_DeviceCreated(f) = deviceCreated.AddHandler(f)

By on 3/1/2008 2:37 AM ()

Thanks Don: this code works beautifully!

I have one remaining niggle though. The properties and members implemented in the interfaces of this class (e.g. GraphicsDevice) are not accessible from either ordinary members of this GraphicsDeviceService or from outside the class. The external code needs to use these.

For the time being I have worked around the problem by implementing everything as ordinary members (outside the interfaces) and calling those ordinary members from members of the same name in the interface members.

Is there a better way to do this? Why are my members being made private? I've tried sticking "public" everywhere but that seems to make no difference...

By on 3/1/2008 4:41 AM ()

Yes, there is currently a requirement to implement interfaces separately from public members. It is a problem that is often encountered when transliterating C# code, though less often when working with F# code alone.

One approach (often possible) is to just to pass around an object of the interface type, i.e. avoid creating a new type at all and just implement via a an object expression instead (or you can still use a class but immediately upcast to the interface type after creating an instance of the class), e.g.

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
 

#light

#nowarn "57"

#light

#I @"C:\Program Files\Microsoft XNA\XNA Game Studio\v2.0\References\Windows\x86"

#r "Microsoft.Xna.Framework.dll"

#r "Microsoft.Xna.Framework.Game.dll"

open System
open System.Windows.Forms
open Microsoft.Xna.Framework

let GraphicsDeviceService(control : Control) =

    let mutable device : Graphics.GraphicsDevice = null

    let _, deviceDisposing = IEvent.create_DelegateEvent()
    let _, deviceReset = IEvent.create_DelegateEvent()
    let _, deviceResetting = IEvent.create_DelegateEvent()
    let _, deviceCreated = IEvent.create_DelegateEvent()

    { new Graphics.IGraphicsDeviceService with

        member this.GraphicsDevice

            with get() = device

        member this.remove_DeviceDisposing(f) = deviceDisposing.RemoveHandler(f)
        member this.add_DeviceDisposing(f)    = deviceDisposing.AddHandler(f)

        member this.remove_DeviceReset(f) = deviceReset.RemoveHandler(f)
        member this.add_DeviceReset(f) = deviceReset.AddHandler(f)

        member this.remove_DeviceResetting(f) = deviceResetting.RemoveHandler(f)
        member this.add_DeviceResetting(f) = deviceResetting.AddHandler(f)

        member this.remove_DeviceCreated(f) = deviceCreated.RemoveHandler(f)
        member this.add_DeviceCreated(f) = deviceCreated.AddHandler(f) }

However, if you need to publish any "additional" members, e.g. access to get/set the device, then this approach won't work so well, and I suspect that's the case above. In that case you might consider defining an interface type that extends IGraphicsDeviceService and contains your extra functionality,and implementing that using the above technique, or just resorting to using double implementation as you did .

C# supports implicitly implementing interfaces using public members on the type. We're considering adding this to F#, under the aim of "making F# an even better OO language". There are also some other great features we're considering in this zone which we have to weigh up, some of which have been discussed on hubFS and the F# list.

Kind regards

don

By on 3/1/2008 10:24 AM ()

Awesome. Thanks, Don. I was only worried because I was afraid this wasn't possible in F#. I don't mind that this is slightly longer in F# because this is such an obscure corner case. I'd value lots of other things over tweaking OO here.Many thanks,
Jon.

By on 3/2/2008 7:16 AM ()
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