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.

+3


source to share


3 answers


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")

      

+6


source


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.

+2


source


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()

      

+1


source







All Articles