This snippet of F# code
let rec reformat = new EventHandler(fun _ _ ->
b.TextChanged.RemoveHandler reformat
b |> ScrollParser.rewrite_contents_of_rtb
b.TextChanged.AddHandler reformat
)
b.TextChanged.AddHandler reformat
results in the following warning:
traynote.fs(62,41): warning FS0040: This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference. This is because you are defining one or more recursive objects, rather than recursive functions. This warning may be suppressed by using '#nowarn "40"' or '--nowarn:40'.
Is there a way in which the code can be rewritten to avoid this warning? Or is there no kosher way of having recursive objects in F#?
Objects can have other objects as attribute values. When an object of some class has an attribute value of that same class, it is a recursive object.
A recursive case has three components: divide the problem into one or more simpler or smaller parts of the problem, call the function (recursively) on each part, and. combine the solutions of the parts into a solution for the problem.
Simple examples of a recursive function include the factorial, where an integer is multiplied by itself while being incrementally lowered. Many other self-referencing functions in a loop could be called recursive functions, for example, where n = n + 1 given an operating range.
The recursive step is the set of all cases where a recursive call, or a function call to itself, is made. The base case is , which is trivial to compute: . In the recursive step, n is multiplied by the result of a recursive call to the factorial of . TRY IT!
Your code is a perfectly fine way to construct a recursive object. The compiler emits a warning, because it cannot guarantee that the reference won't be accessed before it is initialized (which would cause a runtime error). However, if you know that EventHandler
does not call the provided lambda function during the construction (it does not), then you can safely ignore the warning.
To give an example where the warning actually shows a problem, you can try the following code:
type Evil(f) =
let n = f()
member x.N = n + 1
let rec e = Evil(fun () ->
printfn "%d" (e:Evil).N; 1)
The Evil
class takes a function in a constructor and calls it during the construction. As a result, the recursive reference in the lambda function tries to access e
before it is set to a value (and you'll get a runtime error). However, especially when working with event handlers, this is not an issue (and you get the warnning when you're using recursive objects correctly).
If you want to get rid of the warning, you can rewrite the code using explicit ref
values and using null
, but then you'll be in the same danger of a runtime error, just without the warning and with uglier code:
let foo (evt:IEvent<_, _>) =
let eh = ref null
eh := new EventHandler(fun _ _ ->
evt.RemoveHandler(!eh) )
evt.AddHandler(!eh)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With