your initial code was perfectly valid. You don't have to define a method first that calls through to a base.
This works:
do
this.DoSomething()
member this.DoSomething() = base.DoSomething()
but this avoids duplication like you mentioned:
do
base.DoSomething()
This is what was tripping you up -- constructors can not have a return value. If the last statement in a sequence has a return, F# assumes that the function has a return of whatever that value/type is. However, this can not act contrary to a method signature if explicitly defined. So F# asks you to be explicit about your intention in such a case. If your intention is to throw away the return of base.DoSomething(), use the pipe-to-ignore operator combination of |> ignore like this:
do
base.DoSomething() |> ignore
Let's put this another way, if a functions' purpose is to return a value with no side effects, and if this value is consequently not used, then we can safely conclude that the function doesn't need to be called in the constructor. So the compiler / interactive environment warns you about that. That's what F# is encouraging here.
To be fair, that's not immediately obvious, unless you take the following into account: do compiles all such statements into the primary constructor. If you check out the F# OO-style for constructor overloading it might be clearer where the unit * signature was coming from.
Normally, having implicitly defined return value makes writing function a more fluid experience... as Rich Hickey puts it, it is just down to familiarity now.
- another name for unit in other languages is void
Update
Possibly a compiler constraint is being applied in an overeager fashion, or probably behind-the-scenes the do is defined as a closure before getting unwound and applied to the constructor by the compiler. The closure would be able to see the this, but a method / constructor gets a this and a base. That's not very intuitive is it? It seems like you've found a rough edge that needs some sanding. Please consider making a feature request. F# is now completely open source, so it is worthwhile documenting cases like this.
I did a short exercise on this. While this can be used in a constructor, that will not be adequate when there's an override. I think the following might be a decent way 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")