F# application structure logging / repositories etc

This is a basic answer. Firstly, it seems you're thinking of classes and modules as being interchangeable. Classes encapsulate data, and in that sense are more analogous to records and DUs. Modules, on the other hand, encapsulate functionality (they're compiled to static classes). So, I think you already mentioned your options: partial function application, passing the functions around as data, or... dependency injection. For your particular case it seems easiest to keep what you have.

An alternative is using preprocessor directives to include different modules.

#if TESTING 
open LogA
#else
open LogB
#endif

DI isn't necessarily a poor fit in a functional language. It's worth pointing out, F# makes defining and fulfilling interfaces even easier than, say, C#.


If you won't need to change the logger at runtime, then using a compiler directive or #if to choose between two implementations of a logger (as suggested by Daniel) is probably the best and the simplest way to go.

From the functional point of view, dependency injection means the same thing as parameterizing all your code by a logging function. The bad thing is that you need to propagate the function everywhere (and that makes the code a bit messy). You can also just make a global mutable variable in a module and set it to an instance of some ILogger interface - I think this is actually quite acceptable solution for F#, because you need to change this variable only in a couple of places.

Another (more "pure") alternative would be to define a workflow (aka monad) for logging. This is a good choice only if you write all your code in F#. This example is discussed in Chapter 12 of my book, which is available as a free sample. Then you can write something like this:

let write(s) = log {
  do! logMessage("writing: " + s)
  Console.Write(s) }

let read() = log {
  do! logMessage("reading")
  return Console.ReadLine() }

let testIt() = log {
  do! logMessage("starting")
  do! write("Enter name: ")
  let! name = read()
  return "Hello " + name + "!" }

The good thing about this approach is that it is a nice functional technique. Testing code should be easy, because a function that returns Log<'TResult> essenitally gives you a value as well as a record of its side-effects, so you can just compare the results! However, it may be an overkill, because you'd have to wrap every computation that uses logging in the log { .. } block.