Is there any way to make the following code work?
open System.Collections.Generic
type Geometry<'t>(child: 't) =
let values = List()
member o.add (v: float) = values.Add v; child
and Line() as self =
inherit Geometry<Line>(self)
member o.length v = o.add v
let line = Line().length(50.0)
I get
System.NullReferenceException: Object reference not set to an instance of an object.
EDIT:
It is enough to call the following to trigger the exception.
let line = Line()
The motivation is that you can do e.g.:
let line = Line().x1(10).y1(20).x2(30).y2(10).color("blue") // ...
and you can reuse common members among all geometries (circle, ellipse, ...)
open System.Collections.Generic
type Geometry<'t when 't :> Geometry<'t>>() =
let values = List()
member o.add (v: float) = values.Add v; o :?> 't
and Line() =
inherit Geometry<Line>()
member o.length v = o.add v
let line = Line().length(50.0)
In .NET (and by extension F#) you need to initialize an object before you can call any method on it or pass it to another method. Thus, your Line
object would need to be initialized before it is passed to the base constructor Geometry<Line>(self)
. But it can't be initialized before the base constructor is called, so there's no direct way to do what you want.
Having said that, F# has a system that is supposed to catch illegal recursive uses of values before they're defined, so I'm surprised you don't get a more meaningful exception instead of a NullReferenceException
(or better yet, a compile-time error). Compare, for example, what happens if you try to call new Rec()
with the following definition:
type Rec(r:Rec) =
new() as self = Rec(self)
One way around this problem is should be use laziness to delay the use of the self
identifier:
type Geometry<'t>(child: Lazy<'t>) =
let values = List()
member o.add (v: float) = values.Add v; child.Value
and Line() as self =
inherit Geometry<Line>(lazy self)
member o.length v = o.add v
Sadly, this doesn't work, lending further credence to the theory that there's an F# bug with checking the initialization soundness in this case. Fortunately, this can be worked around by using an explicit constructor for Line
instead of a primary constructor:
type Geometry<'t>(child: Lazy<'t>) =
let values = List()
member o.add (v: float) = values.Add v; child.Value
and Line =
inherit Geometry<Line>
new() as self = { inherit Geometry<Line>(lazy self) }
member o.length v = o.add v
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With