39

I'm a bit confused as to how to get two method to call each other (i.e., have A() call B() and B() call A()). It seems that F# only 'sees' the method after it's been encountered in code, so if it hasn't, it just says value or constructor has not been defined.

Am I missing something very basic here?

Brian
  • 117,631
  • 17
  • 236
  • 300
Dmitri Nesteruk
  • 23,067
  • 22
  • 97
  • 166

4 Answers4

47

'let rec... and...' is the syntax you seek.

let rec F() = 
    G()
and G() =
    F()

See also Adventures in F# Co-Recursion.

Abel
  • 56,041
  • 24
  • 146
  • 247
Brian
  • 117,631
  • 17
  • 236
  • 300
  • Best solution. However, if we need to **have A() call B() and B() call A()** then there is something wrong with our functional design. Ideally, we shouldn't encounter this situation. Correct me if I am wrong. – ozgur Oct 23 '18 at 13:46
  • 2
    @ozgur imagine you want to create a JSON parser. Then you would need a function that parses lists whose elements can be objects. And you need a function that parses objects whose values can be lists. This type of recursion isn't as rare as you might think. – The Hoff Feb 10 '19 at 15:08
22

Since the question is about methods, and Brian's answer is about functions, maybe it's useful to point out that you can use a similar syntax for types:

type A() =
    let b = new B()
    member x.MethodA() = b.MethodB()
and B() =
    member x.MethodB() = ()

Note also that members are 'let rec' by default (in fact I don't think they can be not recursive).

Kurt Schelfthout
  • 8,880
  • 1
  • 30
  • 48
10

F# 4.1 introduces mutually recursive modules and namespaces.

These are an alternative to the and keyword.

module rec PingPong = // <------ rec keyword here.

    let pong() = 
        printfn "pong"
        ping() 

    let ping () = 
        printfn "ping"
        pong()

The rec keyword defines modules and namespaces that "allow for all contained code to be mutually recursive."

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
6

Functions declared via let

let rec a () = b ()
and b () = ()

These are mutually recursive functions.

Methods within the same type

type T () =
    member t.A () = t.B()
    member t.B () = ()

This is trivial; it just works. Note Abel's comment though.

Methods within different types

type TypeA () =
    member t.A (b : TypeB) = b.B()

and TypeB () =
    member b.B () = ()

This uses the type ... and syntax for mutually recursive types.

Notes

Normally, and is only used if the calls occur in both directions. Otherwise, it may be better to re-order the declarations so that the called function comes first. It is often helpful for type-inference and readability to avoid circular dependencies, and to not imply them where they aren't used.

I propose to edit the question to either ask for functions in general, or to ask for different types (in which case I would remove the first two cases from this answer). Methods are usually considered to be a subset of functions, which is the general mathematical term. However, all F# functions are technically CLI methods, as that is what they are compiled to. As is, it is not clear what the question is asking for, but I assume from the accepted answer that it does not only ask for methods, as the title would imply.

Vandroiy
  • 6,163
  • 1
  • 19
  • 28
  • 1
    On _Methods within different types_ you say it is "trivial; it just works", but there's a subtlety that's easily overlooked: when you use `inline` and/or certain member constraints it may require you to declare them in order (i.e., backward references will yield strange and hard-to-diagnose errors). See bug report [Order of members of a type is relevant](https://github.com/Microsoft/visualfsharp/issues/1565) – Abel Dec 22 '16 at 03:01