Search code examples
f#mutual-recursion

Split huge F# file with mutually recursive functions


In F# I have a very long code file like

    let rec f1 a1 a2 a3 a4 a5 .. aN =
            ...

    and f2 a1 a2 a3 a4 a5 ... aN =
            ...
    and f3 a1 a2 a3 a4 a5 ... aN =
            ...



    and f40 a1 a2 a3 a4 a5 ... aN =
            ...

In other words there are many mutually recursive functions, each with a lot of parameters.

Now the problem is that the file is 17000 lines long, and Visual Studio has become too slow. (For example, I can't hover the mouse over an item to see its type; if I press the dot, there is no completion, and so on)

Therefore I need to split the file into many smaller files. But I can't see a mechanical and easy way to do it.

Could you please give me an advice? I would like a mechanical way to split the code into multiple files, which does not involve writing the type of all functions (preserve type inference).


Solution

  • In the meantime I found a solution (tested):

    This is the initial situation (simplified to only have four functions, but in reality they are many more):

        let rec f1 a b c  =
    
                f2 a b c;
                f3 a b c;
                f4 a b c;
    
    
    
        and f2 a b c  =
    
                f1 a b c;
                f3 a b c 
                f4 a b c 
    
        and f3 a b c  =
    
                f1 a b c;
                f2 a b c 
                f4 a b c 
    
    
        and f4 a b c  =
                f1 a b c;
                f2 a b c 
                f3 a b c 
    

    And here is the solution:

    Suppose you decide to move f3 to another file. Then you can split the file above in two files as follows:

        FILE 1
        ======
    
    
        let callRef mf =
                match !mf with
                | None -> failwith "function ref is none"
                | Some f -> f
    
        let r_f3 = ref None;
    
        let rec f1 a1 a2 a3  =
    
                f2 a b c;
                callRef r_f3 a1 b1 c1;
                f4 a1 b1 c1;
    
    
    
        and f2 a1 a2 a3  =
    
                f1 a b c;
                callRef r_f3 a1 b1 c1;
                f4 a1 b1 c1;
    
    
    
        and f4 a1 a2 a3 =
                f1 a b c;
                f2 a1 b1 c1;
                callRef r_f3 a1 b1 c1;
    
    
    
    
    
        FILE 2
        ======
    
        let  f3 a1 a2 a3  =
    
                f1 a b c;
                f2 a1 b1 c1;                               
                f4 an bn cn;
    

    Then, in the main initialization function (which is in a third file), you need to do

     r_f3 := Some f3;
    

    And that's it.

    Repeat the same strategy to move f1, f2 and f4 out of the first file.

    Update: This solution works well for functions which return unit, but unfortunately for functions which return an actual type it forces you to specify the function type explicitely, e.g.

    let (r_f3 : (t1 -> t2 -> t3 -> t4 -> t5) option ref)  = ref None;
    

    or you can do this:

    let (r_f3 : 'a option ref)  = ref None;
    

    but you'll get a compiler warning.