Given
type A() =
member val Prop: int = 0 with get, set
There are multiple ways to create an instance
let a0 = A() // Prop = 0
let a1 = A(Prop = 1)
let a2 = A()
a2.Prop <- 2
Now we want to enhance our class and allow passing the prop value in the constructor, but without losing the parameterless constructor we already have
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) = A1(Prop = prop) //Error
However this is an error
This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.
which doesn't seem correct as the new constructor is actually calling an alternate constructor.
There are alternatives/workarounds to achieve the result, for example:
type A2() =
member val Prop: int = 0 with get, set
static member Create(prop: int) = A(Prop = prop)
let a21 = A2.Create(1)
type A3(?prop: int) as this =
do if prop.IsSome then this.Prop <- prop.Value
member val Prop: int = 0 with get, set
let a31 = A3(1)
however the A1 version seems the cleanest and there are no apparent reasons why it cannot be valid (it is very similar to the A2 static member)
Can someone explain why the A1 syntax cannot be valid?
2 Answers 2
The primary constructor of your object should generally take all the parameters required for constructing a valid object. As a general rule I'd make the secondary constructor parameterless and the primary take all the parameters you care about.
If you must have a parameterless primary constructor but still want to assign properties in secondary constructors you can use the then
keyword for side-effectful construction.
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) as this =
A1()
then
this.Prop <- prop
-
Ah, I overlooked the "then" keyword/syntax. Then the strategy with private constructors isn't as useful as I thought, but maybe in some cases.Bent Tranberg– Bent Tranberg06/04/2022 18:04:28Commented Jun 4, 2022 at 18:04
I don't know if it cannot be valid if somebody decides the compiler should handle it, but I thought you might be interested in how we normally solve this case.
type A4(prop: int) =
member val Prop = prop with get, set
new() = A4(0)
let a4a = A4() // 0
let a4b = A4(3) // 3
let a4c = A4(Prop=7) // 7
Following this, it's useful to know how to make private constructors.
type A4 private (prop: int) =
member val Prop = prop with get, set
private new () = A4(0)
This example is of course rather useless code, but it shows where to put the private
keyword. As you probably already understand, you can use one or more private constructors, typically with many parameters, as helpers for public constructors that have fewer parameters.