Thomas
Thomas

Reputation: 12107

is there a use case for the compiler to allow this? in F#

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

Answers (1)

Brian Berns
Brian Berns

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

Related Questions