Reputation: 12107
I made a typo between "High" and "Height" (I blame the auto complete :)) and I ended up with a stack overflow:
// get the zone height
member this.Height =
this.Height - this.Low
which should have read:
member this.Height =
this.High - this.Low
Is there a legitimate case where the compiler should allow this instead of printing an error?
Upvotes: 1
Views: 86
Reputation: 17038
This is allowed because members can be referenced out of order, unlike plain F# values:
type MyType() =
member this.A = this.B + this.B // forward reference to member B
member this.B = 1
Hence, members can be recursive without needing an explicit rec
keyword. I think the rationale here is to conform to the way members work in other .NET languages, such as C#.
To answer your question more specifically, recursive values are unusual, but can be occasionally useful in lazy situations. For example, here's an elegant (but inefficient) infinite sequence of Fibonacci numbers:
type InfiniteSeq() =
static member Fib =
seq {
yield 1
yield 1
yield! Seq.zip InfiniteSeq.Fib (Seq.tail InfiniteSeq.Fib)
|> Seq.map (fun (x, y) -> x + y)
}
InfiniteSeq.Fib
|> Seq.take 10
|> Seq.toArray
|> printfn "%A" // [|1; 1; 2; 3; 5; 8; 13; 21; 34; 55|]
Note that if you try this trick with a plain value (instead of a member), you do get a scary compiler warning:
let rec fib =
seq {
yield 0
yield 1
yield! Seq.zip fib (Seq.tail fib)
|> Seq.map (fun (x, y) -> x + y)
}
// Warning FS0040 This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference. This is because you are defining one or more recursive objects, rather than recursive functions. This warning may be suppressed by using '#nowarn "40"' or '--nowarn:40'.
For another cool (but perhaps not very practical) use case, consider Power series, power serious.
Upvotes: 2