1

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).

seguso
  • 2,024
  • 2
  • 18
  • 20
  • If you have `let ... and ...` it must all be in the same file – John Palmer Aug 29 '16 at 11:12
  • of course, but there must be some way to rewrite the code so as to split it. Declare a function type before declaring the function body, something like that. If I need to do this only for one or two functions, it's ok. – seguso Aug 29 '16 at 11:16
  • Cannot be done, F# doesn't have anything like header files. – John Palmer Aug 29 '16 at 11:17
  • What about F# classes? could they provide a way to overcome the problem? How does C# do it? – seguso Aug 29 '16 at 11:17
  • C# has partial classes which could solve this, but cannot be done in F# – John Palmer Aug 29 '16 at 11:18
  • I am sure there must be a workaround, using function references, something like that. Instead of calling f, you call !f. – seguso Aug 29 '16 at 11:21
  • Again, if you functions truly are mutually recursive this cannot be fixed - the fix is to avoid the use of `let rec .. and ...`. – John Palmer Aug 29 '16 at 11:35
  • 5
    ??? Having *that* many mutually-recursive functions sounds like a design smell to me. Are they **truly** necessary? Is there any way you can refactor your design to extract some common functionality into *non*-mutually-recursive functions, which could then be put into a separate file? I feel like the correct answer is "refactor that ugly design into something more elegant", but without knowing more about your code I can't give you more specific help than that. – rmunn Aug 29 '16 at 12:05

1 Answers1

1

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.

seguso
  • 2,024
  • 2
  • 18
  • 20