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
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
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
... (I don't need to override
as it is already inherited.)
The reason F # has such a limitation is explained here:
But it's still not clear to me why
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.
do this.DoSomething() member this.DoSomething() = base.DoSomething()
but this avoids duplication, as you mentioned:
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
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).
[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
type MyInherited2() = inherit MyBase() let p = base.Param do (fun () -> p) () |> ignore
The problem in your example with MyInherited had to do with what
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