tags:

views:

118

answers:

4

For recursion in F#, existing documentation is clear about how to do it in the special case where it's just one function calling itself, or a group of physically adjacent functions calling each other.

But in the general case where a group of functions in different modules need to call each other, how do you do it?

A: 

This is not supported. One evidence is that, in visual stuido, you need to order the project files correctly for F#.

It would be really rare to recursively call two functions in two different modules.

If this case does happen, you'd better factor the common part of the two functions out.

Yin Zhu
+4  A: 

I don't think there is a way to achieve this in F#. It is usually possible to structure the application in a way that doesn't require this, so perhaps if you described your scenario, you may get some useful comments.

Anyway, there are various ways to workaround the issue - you can declare a record or an interface to hold the functions that you need to export from the module. Interfaces allow you to export polymorphic functions too, so they are probably a better choice:

// Before the declaration of modules
type Module1Funcs = 
  abstract Foo : int -> int
type Module2Funcs = 
  abstract Bar : int -> int 

The modules can then export a value that implements one of the interfaces and functions that require the other module can take it as an argument (or you can store it in a mutable value).

module Module1 = 
  // Import functions from Module2 (needs to be initialized before using!)
  let mutable module2 = Unchecked.defaultof<Module2Funcs>

  // Sample function that references Module2
  let foo a = module2.Bar(a)

  // Export functions of the module
  let impl = 
    { new Module1Funcs with 
        member x.Foo(a) = foo a }

// Somewhere in the main function
Module1.module2 <- Module2.impl
Module2.module1 <- Module1.impl

The initializationcould be also done automatically using Reflection, but that's a bit ugly, however if you really need it frequently, I could imagine developing some reusable library for this.

In many cases, this feels a bit ugly and restructuring the application to avoid recursive references is a better approach (in fact, I find recursive references between classes in object-oriented programming often quite confusing). However, if you really need something like this, then exporting functions using interfaces/records is probably the only option.

Tomas Petricek
Indeed, as you say, it's not needed very often -- mainly I wanted to make sure it won't be a showstopper if and when it does arise. Exporting interfaces looks like a good enough workaround for rare cases.
rwallace
And I didn't realize how bad the situation was, that this flaw applies not only to mutually recursive functions, but all inter-module references. It's a shame, because it's otherwise a beautiful language, and exactly what I would want for my current project.
rwallace
@rwallace: Many people agree that this is a limitation (and it certainly is), however I think that well-desgined functional programs very often avoid the issue. Perhaps if you described your project (in a high-level way), someone could provide hints on the design (to suggest how to avoid hitting this issue).
Tomas Petricek
Having slept on it, I think you're right, a strongly functional design like I have in mind for my current project (a theorem prover) should be able to avoid it. It would be more of a problem for designs with a significant object-oriented component. I gather they are considering addressing this limitation in a future version.
rwallace
+1  A: 

I don't think that there's any way for functions in different modules to directly refer to functions in other modules. Is there a reason that functions whose behavior is so tightly intertwined need to be in separate modules?

If you need to keep them separated, one possible workaround is to make your functions higher order so that they take a parameter representing the recursive call, so that you can manually "tie the knot" later.

kvb
A: 

If you were talking about C#, and methods in two different assemblies needed to mutually recursively call each other, I'd pull out the type signatures they both needed to know into a third, shared, assembly. I don't know however how well those concepts map to F#.

AakashM