Natalie Perret
Natalie Perret

Reputation: 8997

What is a standard type event for F#?

I am trying to subscribe / unsubscribe to a C#-made (in the RabbitMQ client library) from F#:

AsyncEventingBasicConsumer.cs

public event AsyncEventHandler<BasicDeliverEventArgs> Received;

AsyncEventHandler.cs

public delegate Task AsyncEventHandler<in TEvent>(object sender, TEvent @event) where TEvent : EventArgs;

I managed to subscribe to the event by doing:

let subscribeAsync (channel: IModel) exchange callback =
    let consumer = AsyncEventingBasicConsumer(channel)
    consumer.add_Received(AsyncEventHandler<BasicDeliverEventArgs>(fun sender args -> Task.CompletedTask))
    // ...

That being said, I'm wondering why the code below cannot compile:

let subscribeAsync (channel: IModel) exchange callback =
    let consumer = AsyncEventingBasicConsumer(channel)
    consumer.Received.AddHandler(AsyncEventHandler<BasicDeliverEventArgs>(fun sender args -> Task.CompletedTask))
    // ...

Cause I get the error below:

Program.fs(10, 14): [FS1091] The event 'Received' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_Received and remove_Received methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<_>' or 'IEvent<_,_>'.

I checked the official MS documentation but I can't see any reference to what is a standard event type for F#.

Upvotes: 2

Views: 212

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

F# will expose .NET events as values of type IEvent or IDelegateEvent, depending on what the event declaration and delegate type look like. This can only be done for events that have some basic common structure - when F# cannot do this, it will expose the underlying add and remove operations of the event as methods that you can call directly.

I'm not exactly sure what the rules for what counts as "standard event type" are. However, you can get some hint from the relevant bit of the F# compiler source code:

let TryDestStandardDelegateType (infoReader: InfoReader) m ad delTy =
    let g = infoReader.g
    let (SigOfFunctionForDelegate(_, compiledViewOfDelArgTys, delRetTy, _)) =
        GetSigOfFunctionForDelegate infoReader delTy m ad
    match compiledViewOfDelArgTys with 
    | senderTy :: argTys when (isObjTy g senderTy) && 
         not (List.exists (isByrefTy g) argTys)  -> Some(mkRefTupledTy g argTys, delRetTy)
    | _ -> None

So, my guess is that a "stadnard event type" needs to:

  • Have at least one parameter
  • The first parameter has to be of type object
  • There are no byref parameters

Upvotes: 3

Related Questions