[F#][sharppcap][Error] "A type instantiation involves a byref type." what is a workaround in F# - f#

I try to use SharpPcap in F#, but I was blocked by this compiler error for two days.
I find the most releate answer is What is the error "A type instantiation involves a byref type." and what is a workaround in F#, but do not fit my context well.
Please help me work around it, Thank you!
open System
open SharpPcap
open SharpPcap.LibPcap
let device = new CaptureFileReaderDevice("test.pcap")
// try workaround 1
let new_package (sender: Object) (e: PacketCapture) = ()
let handler = new PacketArrivalEventHandler(new_package)
device.OnPacketArrival.AddHandler(handler)
// error: A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
// try workaround 2
let new_package (e: PacketCapture) = ()
device.OnPacketArrival.Add(new_package)
// error: A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
I think the error is because:
public event PacketArrivalEventHandler OnPacketArrival;
public delegate void PacketArrivalEventHandler(object sender, PacketCapture e);
public readonly ref struct PacketCapture
{...}
The delegate PacketArrivalEventHandler use PacketCapture as param type, but this is a readonly ref struct which can not use to define F# function to add to Event OnPacketArrival.
Pls help me workaround it, Thank you!
I want make this line pass the F# compiler:
device.OnPacketArrival.Add(new_package)
In c# it used in this way:
device.OnPacketArrival += new_package
[FS0412] A type instantiation involves a byref type. This is not permitted by the rules of Common IL.

You can't do this by using the F# IEvent first-class listening points, because in this case the type of device.OnPacketArrival ends up being IEvent<PacketArrivalEventHandler, ref<PacketCapture>>, whose second type parameter ref<PacketCapture> is not allowed. This is what the error message tells you.
But you can use the underlying .NET add_ and remove_ methods, which are analogs of property get_ and set_ methods, but for events. F# allows you to call these "hidden" methods explicitly, even though they're not listed in IDE completion lists.
device.add_OnPacketArrival handler
It works, because it's directly calling a method on the device object, rather than creating a wrapping value of type IEvent<...> and then calling .Add or .AddHandler on it.

Related

Type conflicts with Event Listeners

I am trying to attach an event handler to a DOM node using Fable:
let handleIntroSubmit (event: Event) = ()
container
.querySelector("#some-node")
.addEventListener("click", handleIntroSubmit)
However this fails with following error:
This error is baffling for me because:
Looking at the source of Fable.Import.Browser I find EventListenerOrEventListenerObject defined as such:
EventListenerOrEventListenerObject =
U2<EventListener, EventListenerObject>
And EventListener defined as such:
EventListener = (Event -> unit)
Looking at this definition I would have assumed that handleIntroSubmit would be compatible with EventListener and therefore with the union type EventListenerOrEventListenerObject ?
However, to my surprise I discovered that it isn't actually compatible with EventListener, even though their signatures would seem identical to me. When I try the following:
let listener: EventListener = handleIntroSubmit
I get following error:
This expression was expected to have type
'EventListener'
but here has type
''a -> unit'
This error makes no sense to me, in particular I have no idea where does the 'a generic type comes from despite the fact that I have provided an explict type annotation for function handleIntroSubmit argument.
I would ideally like to know why the above does not work, what the error means here and what is the best/typesafe (without unbox) way to add event listener using Fable.
You have two separate issues here.
Firstly, the definition of EventListener that you linked only appeared in master two days ago. Before that, the definition was Func<Event, unit>, which is a .NET delegate, and not at all the same as F# function Event -> unit. From the error message, it is apparent that you're using an earlier version of the library.
To fix this, redefine your function like this:
let handleIntroSubmit = Func<Event, unit>( fun e -> () )
This will give it the type of Func<Event, unit>, which is what EventListener is (currently).
Secondly, even if handleIntroSubmit is compatible with EventListener, it does not mean that it will also be compatible with U2<EventListener, _>. That's a whole different type, why would it be compatible?
To produce a value of U2<EventListener, _> from a value of EventListener, use the first constructor of the U2 union - Case1:
let listener: U2<EventListener, EventListenerObject> = Case1 handleIntroSubmit
Of course, writing Case1 every time is a bit tedious. Thankfully, for dealing with such cases, Fable's core library provides a handy operator !^:
let listener: U2<EventListener, EventListenerObject> = !^handleIntroSubmit
Relevant discussion on Fable's gitter.
It was resolved in the solution provided by Maxime Mangel - relevant fiddle.
btn.addEventListener("click", !^(Func<_,_>(fun _ -> console.log "clicked2")))

f# function not recognized as parameter

Please unwrap these type signatures to help me understand why this doesn't work.
Then, if you have a solution, that would be great too.
I have this code and the agent.Post command has the signature Observer.Create<'T>(onNext: Action<'T>) : IObserver<'T>
let reservatinoRequestObserver = Observer.Create agent.Post
interface IHttpControllerActivator with
To my knowledge, this means that Observer.Create should take an Action with a single generic parameter and then return an IObserver.
Now the definition of Post is member MailboxProcessor.Post : message:'Msg ->unit
So... Post is a method, no? It is a method that takes a single parameter no? And it returns void no? So shouldn't it be a candidate for Observer.Create? Isn't that the exact specification of Action<'T>?
Well, somethings up, I get This function takes too many arguments, or is used in a context where a function is not expected:
Help me out... I freely admit I suck at F#
First, agent.Post returns unit, which is a different thing from void. F# will usually convert back and forth between void and unit for you, but they are not the same thing.
Second, F# functions do not implicitly convert to .NET delegates.
But there are some ways to do it:
You can explicitly create the delegate using its constructor:
let o = Observer.Create (new Action<_>( agent.Post ))
Lambdas are nicely wrapped too
let o = Observer.Create (fun msg -> agent.Post msg)
Use F# Rx-wrappers
Also there are a couple of F# wrappers/interop for Rx on nuget - just have a look, I think any will do

Why does the F# compiler give an error for one case but not the other?

I'm working on a platform invoke call from F#, and I am getting a compiler error I really can't make that much sense out of. First, let me show the C signature of what I am doing:
int Foo(
ULONG_PTR *phHandle,
DWORD flags
);
In F#, I think the correct way to invoke this natively is as so:
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
[<Out>]nativeint& phHandle,
uint32 flags
)
If I try to call this in a class, I get a compilation error when calling it like so:
type Class1() =
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
member this.Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
The error is:
A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
Weirdly, when I do this all in a module, the compilation errors go away:
module Module1 =
[<DllImport("somedll.dll")>]
extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
let Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
Why does this compile as a module, but not as a class?
I don't think it's valid to define an extern method within a class in F#.
If you pull up the F# 3.0 language specification and search for DllImport, near the bottom is a table listing some special attributes and how they can be used. The text for [<DllImport>] says:
When applied to a function definition in a module, causes the F# compiler to ignore the implementation of the definition, and instead compile it as a CLI P/Invoke stub declaration.
That seems to indicate that it's only valid to declare extern methods (that use [<DllImport>]) on functions defined in a module; it doesn't say anything about class members though.
I think you're running into a compiler bug. Please submit this code to fsbugs#microsoft.com so they can fix the error message emitted by the compiler -- it should really be giving you an error about defining an extern method in a class since that's not allowed by the language spec.
Whether this is a bug not withstanding, maybe this is what's going on: If APlatformInvokeCall were considered a static member function, that member have a single argument of tuple type. Tuples are compiled into objects of generic type (see here, at the bottom, or 5.1.3 in the spec). In this case that tuple is
System.Tuple<nativeint&, uint32>
But ECMA 335 II.9.4 says you can't instantiate generic types at byref types. This explains the error reported.
This explanation fits the fact mentioned above that Class1 works (well, compiles) if you modify the extern declaration and call to take instead a single argument. It also fits the fact that the module version works, since in that version there is no considering APlatFormInvokeCall a member function.
The simple solution is to check the spec, here is the class definition grammar:
type type-name pat_opt as-defn)opt =
class
class-inherits-decl_opt
class-function-or-value-defns_opt
type-defn-elements
end
then we have
class-function-or-value-defn :
attributes_opt staticopt let rec_opt function-or-value-defns
attributes_opt staticopt do expr
which doesn't allow extern.
and
type-defn-element :
member-defn
interface-impl
interface-signature
which isn't what you want either.
As a result, we can see that using extern as you are trying to use it can't be done inside a class.

Why can't a function with byref be converted directly to delegate?

Under normal circumstances, F# functions can be converted to delegates by calling new DelegateType and passing in the function as an argument. But when the delegate contains byref parameter, this is not possible directly. For example the code:
type ActionByRef<'a> = delegate of 'a byref -> unit
let f (x:double byref) =
x <- 6.0
let x = ref 42.0
let d = new ActionByRef<_>(f)
won't compile, giving the following error:
This function value is being used to construct a delegate type whose signature includes a byref argument. You must use an explicit lambda expression taking 1 arguments.
Following the error, modifying the code to use
let d = new ActionByRef<_>(fun x -> f(&x))
works. But my question is: why is this necessary? Why won't F# allow the conversion from named function to this delegate, but conversion from lambda is fine?
I came upon this behavior when researching another question. I realize byref is meant only for compatibility with other .Net languages.
I think the problem is that byref<'T> is not an actual type in F# - it looks like a type (to make the language simpler), but it gets compiled to a parameter marked with the out flag. This means that byref<'T> can be only used in a place where the compiler can actually use the out flag.
The problem with function values is that you can construct function e.g. by partial application:
let foo (n:int) (b:byref<int>) =
b <- n
When you pass foo as an argument to a delegate constructor, it is a specific case of partial application (with no arguments), but partial application actually needs to construct a new method and then give that to the delegate:
type IntRefAction = delegate of byref<int> -> unit
let ac = IntRefAction(foo 5)
The compiler could be clever and generate new method with byref parameter (or out flag) and then pass that by reference to the actual function, but in general, there will be other compiler-generated method when you don't use the fun ... -> ... syntax. Handling this would add complexity and I think that's a relatively rare case, so the F# compiler doesn't do that and asks you to be more explicit...

F# top level do binding

I have a file with a module with some routines that take parameters and return unit, these routines have side-effects. I noticed that when accessing these f# routines from c# they're actually properties of type unit and when I try to access 1 property, it runs all properties in the module.
From the F# documentation all top level do bindings are run on type initialization.
What is the preferred way to write functions that should not be run on type initialization but are also not associated with other state i.e. a class with functions and member variables?
Should I put these functions inside a type and just have no records in the type?
Code example:
namespace test_space
open System.Diagnostics;
module test =
let test_1 =
Debug.WriteLine ("One")
let test_2 =
Debug.WriteLine ("Two")
I'm running this code with C#:
static void Main (string [] args)
{
Object o;
o = test.test_2;
}
And the output is:
One
Two
The problem is you didn't create functions but value bindings. test_1 is a value. test_1() is a function of type unit -> unit. Make sure you put () after the function name.
I don't fully understand the scenario you're describing - F# functions declared in a module will generally appear as methods and values will appear as properties. The code that is executed when you first access module (type initialization) is the initialization of values.
If you write just:
module Foo =
let Operation () =
printfn "hello"
...then calling Operation will be a method and calling Foo.Operation() will run the side-effect. If you can post some code that behaves unexpectedly, then someone can explain it.
Anyway, if you want to be sure about the behavior, you can write operations as static members of a class:
type Foo =
static member Operation() =
printfn "hello"
Then you can be sure that F# will compile them as static members of a class in a predictable way.

Resources