Events and Delegates Ceremony - f#

Can I take this statement:
let propertyChanged =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
And rename it to:
let propertyChangedDelegate =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
I'm trying to understand the ceremony of event declaration within F#.
Specifically, I am trying to understand the difference between:
let propertyChanged =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
And:
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
Here's the entire scope of code here:
open System.ComponentModel
open Microsoft.FSharp.Quotations.Patterns
type ViewModelBase () =
let propertyChanged =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
let getPropertyName = function
| PropertyGet(_,pi,_) -> pi.Name
| _ -> invalidOp "Expecting property getter expression"
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
member private this.NotifyPropertyChanged propertyName =
propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))
member this.NotifyPropertyChanged quotation =
quotation |> getPropertyName |> this.NotifyPropertyChanged
Can someone please explain to me how events are implemented in F#?

In F#, events are just values of type IEvent<T> that you can pass around. So, to create an event, you need to write some implementation of this interface (which handles adding and removing event handlers).
The Event<T> type provides a default sensible implementation of the interface. When you write:
let propertyChanged =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
You're just using a class from the F# library that creates a new implementation of an event which accepts PropertyChangedEventArgs and can be used with event handlers of the delegate type PropertyChangedEventHandler.
The returned propertyChanged object has two things:
Publish property gives you the IEvent<T> value (representing event)
Trigger method invokes the event (which then invokes all the event handlers that are registered with the IEvent<T> value that is hidden inside the propertyChanged object).
So writing:
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
Is just a way of exposing the event (using the CLIEvent attribute to tell the F# compiler that it should be compiled as a .NET compatible event).
There is a more detailed documentation on F# events on MSDN if you want to learn more.

Related

Why can I pass two parameter to Event class

When I look the definition of event class in f#
type Event<'T> =
class
new Event : unit -> Event<'T>
member this.Trigger : 'T -> unit
member this.Publish : IEvent<'T>
end
I can pass only one type on event class like
let nameChanged = new Event<unit>()
But I saw some sample too, that pass two variables like
let propertyChanged = Event<PropertyChangedEventHandler, PropertyChangedEventArgs>()
How could be that be possible?
And when I implement an interface like
module SapHandler
open SAP.Middleware.Connector
type Connector() =
let configurationChanged = Event<RfcDestinationManager.ConfigurationChangeHandler, RfcConfigurationEventArgs>()
interface IDestinationConfiguration with
member self.ChangeEventsSupported() =
false
[<CLIEvent>]
member self.ConfigurationChanged = configurationChanged.Publish
The compiler complain:
The type 'RfcDestinationManager.ConfigurationChangeHandler' has a non-standard delegate type
Why?
The definition of delegate type is:
It's possible because there's another class too:
type Event<'Delegate,'Args (requires delegate)> =
class
new Event : unit -> Event<'Delegate,'Args>
member this.Trigger : obj * 'Args -> unit
member this.Publish : IEvent<'Delegate,'Args>
end
You're getting that error because your delegate for the event is probably missing the first object sender argument which is standard in .net. You can use Control.DelegateEvent<'Delegate> to get around this
let propertyChanged = DelegateEvent<RfcDestinationManager.ConfigurationChangeHandler>()

Does F# handle inheritance differently compared to C# when type parameters are covariant?

I hit something new to me with the following piece of code when following the equivalent in C# here. The compiler gives multiple errors basically telling the IConnectableObservable created in source.Publish() does not match IObservable even though it derives from it (according to the MSDN article linked).
Is there something in F# that is different with regard to C# concerning inheritance in this case or can someone provider pointers as to what is going on? Have I just made a typo I can't see? What comes to the heading regarding covariance, it's just a wild guess as I'm at least temporarily out of ideas. And so, maybe writing somewhere may help me and others...
One example of the many error messages:
No overloads match for method 'Create'. The available overloads are shown below (or in the Error List window).
No overloads match for method 'Switch'. The available overloads are shown below (or in the Error List window).
Error Possible overload: '(extension) IObservable.Switch<'TSource>() :
IObservable<'TSource>'. Type constraint mismatch. The type
IObservable<IConnectableObservable<'b>> is not compatible with type
IObservable<IObservable<'a>> The type 'IObservable<'a>' does not match the type 'IConnectableObservable<'b>'.
open System.Reactive.Concurrency
open System.Reactive.Disposables
open System.Reactive.Subjects
open System.Reactive.Linq
type Observable with
static member inline Suspendable(source: IObservable<_>, suspend: IObservable<bool>, isSuspendedInitially: bool): IObservable<_> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer), shared.Connect()))
The corresponding C# code
public static class RxExtensions
{
public static IObservable<T> Suspendable<T>(this IObservable<T> stream, IObservable<bool> suspend, bool isSuspendedInitially)
{
return Observable.Create<T>(o =>
{
var shared = stream.Publish();
var pausable = suspend
.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(p => p ? shared : Observable.Empty<T>())
.Switch();
return new CompositeDisposable(pausable.Subscribe(o), shared.Connect());
});
}
}
This was a bit tricky, but you need to add two upcasts: shared to IObservable<_>, and the result of the lambda function to IDisposable. These would be implicit in C#, but need to be explicit in F#:
type Observable with
static member inline Suspendable (source: IObservable<_>,
suspend: IObservable<bool>,
isSuspendedInitially: bool): IObservable<'a> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared :> IObservable<_>
else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer),
shared.Connect()) :> IDisposable)

Implement a C# Interface in F#

I have the following interface in C#
public interface IDog
{
String Bark();
}
I want to create an implementation in F#. I had no problem with this:
type GermanShepherd() =
interface IDog with
member this.Bark() = "Woof"
but when I added a supporting function that is not part of the interface like this:
type GermanShepherd() =
interface IDog with
member this.Bark() = "Woof"
member this.Eat() = "Yummy"
the compiler complained:
Error 1 No abstract or interface member was found that corresponds to this override
Error 2 This value is not a function and cannot be applied*
Is there a way of implementing private/internal functions with a type that implements an interface without those functions being part of the interface? I can't change the interface in my 'real' application b/c there are other projects that implement the interface. None of the examples on MSDN that I found have this particular scenario.
The interface block should only contain functions that are part of the interface, but you can place other functions before the block:
type GermanShepherd() =
member this.Eat() = "Yummy"
interface IDog with
member this.Bark() = "Woof"
If you do not need a public member, then you can go with a private let-bound function:
type GermanShepherd() =
let eat() = "Yummy"
interface IDog with
member this.Bark() = "Woof"
Also note that F# currently only allows explicit interface implementations, which means that you can treat GermanShepherd as IDog, but you won't see IDog members explicitly:
let g = GermanShepherd()
g.Eat() // OK
g.Bark() // Error, interface members are implemented explicitly
let d = g :> IDog // To 'Bark', we need to get 'IDog' first
d.Bark() // OK
One workaround for this is to add the Bark method explicitly as a separate member outside of the interface block. Although there is F# language issue for this too!

Can't correctly implement XNA interface IUpdateable in F# because of events

I'm currently using F# and XNA for my new project, but got stuck when implementing XNA's IUpdateable interface. Specifically when I need to implement the event handlers.
This is what I have:
let enabledChanged = new DelegateEvent<System.EventHandler>()
and further down:
interface IUpdateable with
... // other stuff
[<CLIEvent>]
member self.EnabledChanged = enabledChanged.Publish
But i'm getting this error:
Error 2 This expression was expected to have type EventHandler but here has type EventHandler<EventArgs>
I've tried several things but can't get this to work. What is expected to be there?
Thanks in advance!
Have you tried defining enabledChanged in the following way?
let enabledChanged = new Event<_>()
as I see IUpdatable.EnabledChanged has type System.EventHandler<System.EventArgs> but you are using System.EventHandler without type parameters.
type X() =
let enabledChanged = DelegateEvent<EventHandler<_>>()
interface IUpdateable with
[<CLIEvent>]
member this.EnabledChanged = enabledChanged.Publish
type Y() =
let enabledChanged = Event<EventHandler<_>, _>()
interface IUpdateable with
[<CLIEvent>]
member this.EnabledChanged = enabledChanged.Publish

Can I use the 'use' keyword with object expressions?

I'm using an object expression to implement two interfaces. One of the interfaces is IDisposable. I expected to be able to use the 'use' keyword with the results from this object expression, but I get an error:
Type constraint mismatch. The type
IConnMan is not compatible with type
IDisposable The type 'IConnMan' is not
compatible with the type
'IDisposable'
Why do I get this error?
let connectionstring = "context connection=true"
let connman () =
let conn = new SqlConnection(connectionstring)
conn.Open()
{ new IConnMan with
member x.Connect () = conn
member x.Disconnect c = ()
interface IDisposable with
member x.Dispose() =
conn.Close()
conn.Dispose()
}
...
let f() =
use cn = connman() // <-- Error!
An object expression can have just a single type. The type is the type of the first (main) implemented interface - in your case, that's the IconnMan type. F# doesn't allow you to use use, because it doesn't statically know that the result of connman function is IDisposable.
You could create interfaces in the object expression in the opposite order:
let connman () =
let conn = new SqlConnection(connectionstring)
conn.Open()
{ new IDisposable with
member x.Dispose() =
conn.Close()
conn.Dispose()
interface IConnMan with
member x.Connect () = conn
member x.Disconnect c = () }
Then you could write use cn = connman() but you couldn't use functions of IConnMan without casting (this is essentially the same as doing what Desco suggests). I don't think there is any nice solution to this problem.
Could your IConnMan interface inherit from IDisposable?
F# spec says:
Object expressions are statically checked as follows.
First, ty0 to tyn are checked and must all be named types. The overall type of the expression is ty0 and is asserted to be equal to the initial type of the expression. However, if ty0 is type equivalent to System.Object and where ty1 exists, then the overall type is instead ty1.
so you can use type tests and downcasts or change the sequence of interfaces being implemented so IDisposable will be first
let f() =
use cn = connman() :?> IDisposable

Resources