How to unsubscribe an event from inside the event handler? - f#

My code subscribes to an event and it needs to unsubscribe (Dispose) once the event has been handled. However this looks like a chicken-egg problem. Using rec doesn't work and I cannot find how to do it.
It there any well-konwn pattern to bypass this limitation?
let process = new Process()
let exitSubscription = process.Exited.Subscribe (
fun evntArg ->
exitSubscription.Dispose() <---------- Compiler complains here
// do more something here.
)

(you're saying in your question that rec doesn't work, but do not clarify why or how; so I'm going to ignore that part for this answer)
One way to do this is to declare the value "recursive" with the rec keyword. This will allow the value initialization code to reference the value itself - just like with a recursive function.
let rec exitSubscription : IDisposable = process.Exited.Subscribe (
fun evntArg ->
exitSubscription.Dispose()
// do more something here.
)
This will work, but will also produce a warning saying "this is a recursive value, and I can't tell if you're actually accessing it while it's being constructed, so you have to make sure that you don't, and if you do anyway, it'll be a runtime error".
Note that you also have to add a type signature to the variable, otherwise the compiler can't quite grok it and complains that it might not have a Dispose method.
Another way is to make the variable mutable and then mutate it:
let mutable exitSubscription : IDisposable = null
exitSubscription <- process.Exited.Subscribe (
fun evntArg ->
exitSubscription.Dispose()
// do more something here.
)
Here you have to use a type signature too (for the same reason), and you have to initialize it with null, because there is no such thing as an uninitialized variable.
Also, this is a tiny bit less safe, because the variable is mutable, and that's always a source of bugs. As long as you pinky-promise not to mutate it (beyond this initialization), you're ok.
But a "proper" way is to use an observable combinator to limit the observable to only one element. That way you don't have to unsubscribe explicitly, which is always more reliable:
#r "nuget: System.Reactive"
open System.Reactive.Linq
process.Exited.Take(1).Subscribe (
fun evntArg ->
// do more something here.
)

Related

Clear all event handlers in F#?

If I remember correctly from my C# days, there was a way to unsubscribe all event handlers from an event without the need to do a -= operation for each of them: assigning null to the event.
However, seems that in F# this is not possible? I find the methods Trigger, Publish, Subscribe, Add, and Remove. Would be handy to have a Clear one or something alike. Maybe it's easy to implement a custom event that can achieve this?
As far as I can see, there is no way to do this with standard F# Event<'T> type. Even when you define an event and mark it as CLIEvent, the generated code defines a custom type of event with add and remove methods and so there does not seem to be a way to remove all handlers.
However, if you need to define your own F# event that supports this, you can actually implement this yourself. You can implement something like Event<'T> which keeps track of event handlers using a ResizeArray and removes all handlers when asked to do that. All you need to do is to implement the IObservable (or if you want more methods, IEvent interface).
The following does not handle concurrency correctly and might break, but it shows the idea:
open System
type RemovableEvent<'T> () =
let handlers = ResizeArray<Handler<_>>()
member x.Trigger(v) =
for h in handlers do h.Invoke(x, v)
member x.Clear() =
handlers.Clear()
member x.Publish =
{ new IEvent<'T> with
member x.AddHandler(h) = handlers.Add(h)
member x.RemoveHandler(h) = handlers.Remove(h) |> ignore
member x.Subscribe(h) =
let h = Handler<_>(fun _ v -> h.OnNext v)
handlers.Add(h)
{ new IDisposable with
member x.Dispose() = handlers.Remove(h) |> ignore } }
let re = RemovableEvent<_>()
re.Publish.Add(printfn "Hello %s")
re.Publish.Add(printfn "Hello again %s")
re.Trigger("world")
re.Clear()
This quote really just confirms the answer by Tomas.
From Concurrency in .NET by Riccardo Terrell, Published by Manning Publications, 2018, Chapter 6
"It’s possible to choose (and use) either Observable or Event when using F# to build reactive systems; but to avoid memory leaks, the preferred choice is Observable. When using the F# Event module, composed events are attached to the original event, and they don’t have an unsubscribe mechanism that can lead to memory leaks. Instead, the Observable module provides the subscribe operator to register a callback function. This operator returns an IDisposable object that can be used to stop event-stream processing and to de-register all subscribed observable (or event) handlers in the pipeline with one call of the Dispose method."

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 is F# inferring my function implements IComparable?

I'm a hobbyist programmer (cook by trade) that's currently trying to teach myself F# and functional programming in general.
Anyway, I was fooling around with DeflateStream and wrote the following two functions:
let Compress compressMe =
let ds = new DeflateStream(File.Create("compressed.txt"), CompressionMode.Compress)
File.OpenRead(compressMe).CopyTo(ds)
ds.Close()
let Decompress =
let ds = new DeflateStream(File.OpenRead("compressed.txt"), CompressionMode.Decompress)
ds.CopyTo(File.Create("decompressed.txt"))
ds.Close()
In the body of the main function they are called one right after the other like this:
Compress args.[0]
Decompress
However, if compressed.txt doesn't exist when the program is run Decompress throws a FileNotFoundException which is surprising because the only thing that could throw this is the call to File.OpenRead("compress.txt"). After about an hour I figured out that Decompress was implementing IComparable and was being executed before the call to it in the main function. I found that by changing its definition to let Decompress () = [...] it no longer implemented IComparable and my code executed as it was intended to. Can anyone tell me why F# was infering IComparable and why such and inference would cause the function to execute before the main function marked with [<EntryPoint>]? Also, please forgive the imperitive style of my code, I'm incredibly new at this.
Thanks in adavance.
I'm not entirely sure about the IComparable bit, but the issue you have is that without the parentheses, the compiler is treating Decompress as a value not a function. This would be similar to if you had written.
let compressedName = "compressed.txt"
in that case, compressedName is now a value. Adding the parentheses tells the compiler that this is a function whose code must be called each time the function is rather than a value initialized once (before the entry point) by the code you wrote.
When you write something like
let value =
//some long calculation
[<Entrypoint>]
let main args = ...
The compiler executes the long calculation before main. This is because you probably use the value later in your code. To suppress this, as you found, you need to use let value() = ....
I am not sure where Icomparable is coming from, but this is the key to what is happening.
Note, if you write
let a = ...
let b = ...
The compiler will gurantee a is calculated before b executes.
As others have pointed out, the absence of parentheses in the declaration is significant.
let Decompres = ...
declares a variable of type unit. This type is used to represent "data" (even if this data doesn't encode much information), and it implements IComparable like any other data-oriented type.
let Decompress() = ...
declares a function, and functions in F# are not comparable, probably because there is no universally accepted notion of equality on functions.
I can't see that the "IComparable-ness" of Decompress had anything to do with the exception you got.

Is None less evil than null?

In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.
Is None really less evil than null?
The problem with null is that you have the possibility to use it almost everywhere, i.e. introduce invalid states where this is neither intended nor makes sense.
Having an 'a option is always an explicit thing. You state that an operation can either produce Some meaningful value or None, which the compiler can enforce to be checked and processed correctly.
By discouraging null in favor of an 'a option-type, you basically have the guarantee that any value in your program is somehow meaningful. If some code is designed to work with these values, you cannot simply pass invalid ones, and if there is a function of option-type, you will have to cover all possibilities.
Of course it is less evil!
If you don't check against None, then it most cases you'll have a type error in your application, meaning that it won't compile, therefore it cannot crash with a NullReferenceException (since None translates to null).
For example:
let myObject : option<_> = getObjectToUse() // you get a Some<'T>, added explicit typing for clarity
match myObject with
| Some o -> o.DoSomething()
| None -> ... // you have to explicitly handle this case
It is still possible to achieve C#-like behavior, but it is less intuitive, as you have to explicitly say "ignore that this can be None":
let o = myObject.Value // throws NullReferenceException if myObject = None
In C#, you're not forced to consider the case of your variable being null, so it is possible that you simply forget to make a check. Same example as above:
var myObject = GetObjectToUse(); // you get back a nullable type
myObject.DoSomething() // no type error, but a runtime error
Edit: Stephen Swensen is absolutely right, my example code had some flaws, was writing it in a hurry. Fixed. Thank you!
Let's say I show you a function definition like this:
val getPersonByName : (name : string) -> Person
What do you think happens when you pass in a name of a person who doesn't exist in the data store?
Does the function throw a NotFound exception?
Does it return null?
Does it create the person if they don't exist?
Short of reading the code (if you have access to it), reading the documentation (if someone was kindly enough to write it), or just calling the function, you have no way of knowing. And that's basically the problem with null values: they look and act just like non-null values, at least until runtime.
Now let's say you have a function with this signature instead:
val getPersonByName : (name : string) -> option<Person>
This definition makes it very explicit what happens: you'll either get a person back or you won't, and this sort of information is communicated in the function's data type. Usually, you have a better guarantee of handling both cases of a option type than a potentially null value.
I'd say option types are much more benevolent than nulls.
In F# its a big deal that they do not have null values and do not want to support it. Still the programmer has to make cases for None similar to C# programmers having to check != null.
Is None really less evil than null?
Whereas null introduces potential sources of run-time error (NullRefereceException) every time you dereference an object in C#, None forces you to make the sources of run-time error explicit in F#.
For example, invoking GetHashCode on a given object causes C# to silently inject a source of run-time error:
class Foo {
int m;
Foo(int n) { m=n; }
int Hash() { return m; }
static int hash(Foo o) { return o.Hash(); }
};
In contrast, the equivalent code in F# is expected to be null free:
type Foo =
{ m: int }
member foo.Hash() = foo.m
let hash (o: Foo) = o.Hash()
If you really wanted an optional value in F# then you would use the option type and you must handle it explicitly or the compiler will give a warning or error:
let maybeHash (o: Foo option) =
match o with
| None -> 0
| Some o -> o.Hash()
You can still get NullReferenceException in F# by circumventing the type system (which is required for interop):
> hash (box null |> unbox);;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.UnboxGeneric[T](Object source)
at <StartupCode$FSI_0021>.$FSI_0021.main#()
Stopped due to error

Resources