Call base element in lambda function from inherited class constructor?
I have a base class
type MyBase() =
let param = myfun()
member this.Param = param
type MyInherited() =
inherit MyBase()
do
base.Param.DoSomething() // cannot access base in let or do binding.
I would like to call DoSomething
exact once during the instantiation of the inherited object. But they won't let me in here. What should I do then?
Should I create a method
member this.DoSomething() = base.Param.DoSomething()
and call it in the constructor?
type MyInherited() as self =
inherit MyBase()
do
self.DoSomething()
It's a little weird and duplicated
Update
My initial simplified example doesn't fit. Check the following:
type MyBase() =
let param = "test"
member this.Param = param
type MyInherited() =
inherit MyBase()
do
(fun () -> base.Param) () |> ignore // Doesn't work,
// A protected member is called or 'base' is being used.
// This is only allowed in the direct implementation of members
// since they could escape their object scope.
type MyInherited() as self =
inherit MyBase()
do
(fun () -> self.Param) () |> ignore // Works
Now this is actually much better, all I have to do is use self
instead of base
... (I don't need to override Param
as it is already inherited.)
The reason F # has such a limitation is explained here:
Why is the base only possible in private members?
But it's still not clear to me why base
it can't be used in a closure, even though it's available in a simple let binding.
source to share
your original code was perfectly correct. You don't need to first define the method that calls to the base.
It works:
do
this.DoSomething()
member this.DoSomething() = base.DoSomething()
but this avoids duplication, as you mentioned:
do
base.DoSomething()
This is what turned you off - constructors cannot have a return value. If the last statement in the sequence returns, F # assumes that the function returns any value / type. However, this cannot contradict the method signature if it is explicitly defined. Therefore, F # asks you to clearly state your intent in such a case. If your intention is to discard the return of base.DoSomething (), use the pipe-ignore operator combination | > ignore like this:
do
base.DoSomething() |> ignore
In other words, if the purpose of a function is to return a value without side effects, and if that value will therefore not be used, we can safely conclude that the function doesn 't need to be called in the constructor. Therefore the compiler / interactive environment warns you about this. This is what F # is encouraging here.
To be fair, this is not immediately obvious unless you consider the following: do compiles all such statements into a primary constructor . If you go with the F # OO style for constructor overloading , it might be clearer where the unit * signature comes from.
Usually, if an implicitly defined return value makes the write function more fluid, ... as Rich Hickey put it, now it's just pre-dating.
- another name for unit in other languages void
Update
Perhaps the compiler constraint is being applied in overeager mode, or perhaps behind the scenes, do is defined as a closure before unwinding and applied by the compiler to the constructor. The closure might see this , but the method / constructor gets this and the base . Is it not very intuitive? It looks like you've found a rough edge that needs grinding. Please consider creating a feature request . F # is now completely open source, so it's worth documenting these cases.
I did a short exercise on this. While this one can be used in a constructor, it won't be adequate if there is an override. I think the following might be a good way to go forward (see the new () block at the end).
[<AbstractClass>]
type MyBase() =
let letValue = "let value"
abstract Method : unit -> string
default this.Method() = "test"
member this.Method2() = "Param2"
type MyInherited(param : string) as this =
inherit MyBase()
// let localLetValue() = this.letValue // fails. let values are private members
do
//(fun () -> base.Param) () |> ignore // Error: The 'base' keyword is used in an invalid way. Base calls cannot be used in closures. Consider using a private member to make base calls.
(fun () -> this.Method) () |> ignore // succeeds -- takes local
(fun () -> this.base_Method) () |> ignore // succeeds -- takes base
(fun () -> this.Method2) () |> ignore // succeeds -- takes base
override this.Method() = "ABCdefHIJ"
member this.base_Method() = base.Method()
new() as this =
let a = base.Method() // succeeds
let b = this.Method() // succeeds
MyInherited("some value")
source to share
It works:
type MyInherited2() =
inherit MyBase()
let p = base.Param
do
(fun () -> p) () |> ignore
The problem in your example with MyInherited had to do with what base.Param
was captured in the closure. This works in let binding because, for all intents and purposes of let, the binding in the object constructor is a direct implementation of the member.
But you're right about the sense of inheritance. This is a big worm of worms in F # and it's best to keep it dead simply if you can't avoid it at all.
source to share
Unable to play. For example this works for me:
type MyBase() =
let param = "test"
let param' = fun _ -> printfn "test'"
member this.Param = param
member this.Param' = param'
type MyInherited() =
inherit MyBase()
do
printfn "%s" (base.Param.ToString())
base.Param'()
let inh = new MyInherited()
source to share