dependency injection – a functional way

(TL;DR: Intro to the reader-monad in F# for dependency-injection – see the source)

intro: the problem

Let’s say you have a function f that needs an object implementing some interface

type InterfaceA =
    abstract MethodA : string -> string

to do it’s work.

a dirty solution (not advised)

Another solution that can work is to make the dependency a mutable part of your defining module:

module MyModule =
    let DependencyA = ref DefaultImplementationA

    let f x = ... use (!DependencyA).MethodA ...

I would not advise using this for obvious reasons (hard to test if tests run parallel, etc.) but it can come handy from time to time.
For example I sometimes like to use these for static-parameters/feature-switches and cross-cutting-concerns like logging – but really: use with care.

the usual solution

Using object oriented techniques you would most likely use constructor-injection to solve this:

type ConstructorInjection (i : InterfaceA) =
    member __.f x = ... do something with i.MethodA ...

But isn’t that overkill? We make a new class only to inject the interface. What’s even worse: we now need an instances of ConstructorInjection to even get the f back.

just make it explicit

In most cases I just make the dependencies into explicit arguments and deal use using partial application:

let f (i : InterfaceA) (x : X) : Y = ...
let f` x = f myImplementation x

many dependencies

So what if you have to deal with many dependencies? That’s usually when you start looking for an DI-container to keep track of all the stuff. Keeping it simple you can just collect all dependencies you have in a dependencies structure – for example a record like this:

type Dependencies =
    { interfaceA : InterfaceA
    ; interfaceB : InterfaceB
    // ...

But of course you probably would not want this to leak into your modules everywhere (meaning: don’t use Dependencies as parameters to your functions if they really only need InterfaceA). Instead let the caller handle the projections:

let deps : Dependencies = ...
let f (i : InterfaceA) x = ...
let res = f deps.interfaceA argX

the fancy solution

You might have guessed it already: there is a fancy solution and yeah it’s involving the M-word.

Functions depending on InterfaceA will look something like this: InterfaceA -> additional args -> result. Let’s change this a bit: First flip the arguments into additional args -> InterfaceA -> result which is really additional args -> (InterfaceA -> result). Then extract the common part into a type:

type UsingA<'result> = InterfaceA -> 'result

Now the types work out into additional args -> UsingA<'result> and if you have no additional arguments it will just be an value of UsingA.

Abstracting it further by making the dependency generic also we end up with:

type ReaderM<'dependency,'result> =
    'dependency -> 'result

let run (d : 'dependency) 
        (r : ReaderM<'dependency,'result>) 
        : ' result = r d

and our function now has a new type f : X -> ReaderM<InterfaceA,Y>

implementing the usual patterns

It turns out, that ReaderM falls into several patterns, so let’s start collecting some:

constant readers

It’s useful to have a ReaderM value that does always returns a constant:

let constant (c : 'c) : ReaderM<_,'c> =
    fun _ -> c


ReaderM is a functor – let’s give it a map:

let map (f : 'a -> 'b) 
        (r : ReaderM<'d, 'a>) 
        : ReaderM<'d,'b> =
    r >> f

let (<?>) = map


let apply (f : ReaderM<'d, 'a->'b>)
          (r : ReaderM<'d, 'a>)
          : ReaderM<'d, 'b> =
    fun dep ->
        let f' = run dep f
        let a  = run dep r
        f' a

let (<*>) = apply


Of course ReaderM is a monad 😉

let bind (m : ReaderM<'d, 'a>) 
         (f : 'a -> ReaderM<'d,'b>) 
         : ReaderM<'d, 'b> =
    fun dep ->
        f (run dep m)
        |> run dep

let (>>=) m f = bind m f

type ReaderMBuilder internal () =
    member __.Bind(m, f)    = bind m f
    member __.Return(v)     = constant v
    member __.ReturnFrom(v) = v
    member __.Delay(f)      = f ()

let Do = ReaderMBuilder()

lifting it

Now let’s make it easier to create ReaderM<_,_> valued computations from functions with dependencies (like our f) above by lifting those into the monad:

let lift1 (f : 'd -> 'a -> 'out)
            : 'a -> ReaderM<'d, 'out> =
    fun a dep -> f dep a

let lift2 (f : 'd -> 'a -> 'b -> 'out)
            : 'a -> 'b -> ReaderM<'d, 'out> =
    fun a b dep -> f dep a b

let lift3 (f : 'd -> 'a -> 'b -> 'c -> 'out)
            : 'a -> 'b -> 'c -> ReaderM<'d, 'out> =
    fun a b c dep -> f dep a b c
// ...

And there is another way you can lift stuff: Think back on the situation above where you have a function depending on InterfaceA but you wrapped an instance into a Dependencies structure. Here you can lift a ReaderM<InterfaceA,_> computation into a ReaderM<Dependencies,_> computation when you have an projection from Dependencies -> InterfaceA:

let liftDep (proj : 'd2 -> 'd1) 
            (r : ReaderM<'d1, 'out>) 
            : ReaderM<'d2, 'out> =
    proj >> r


Let’s put this all into a short example:

Imagine you have these two interfaces (and some implementations):

type IResources =
    abstract GetString : unit -> string

let resource = 
    { new IResources with
        member __.GetString () = "World"

type IOutput =
    abstract Print : string -> unit

let output = 
    { new IOutput with
        member __.Print s = printfn "%s" s

We collect them into a Dependencies type – to keep things simple it’s just a tuple of those two:

type Dependencies= IResources * IOutput
let config = (resource, output)

Now we need some functions working with these dependencies:

let getWord =
    lift1 (fun (res : IResources) -> res.GetString) ()

let print =
    lift1 (fun (out : IOutput) -> out.Print)

And our task is to get a word from the resources using the GetString, prepending "Hello " and printing the result using Print from IOutput:

let computation () = Do {
    let! text = sprintf "Hello %s" <?> liftDep fst getWord
    do! liftDep snd (print text)

Sadly I have to make computation into a function (or give F# some further hints by using it – see the gist) or else I will run into a Value restriction (I talked about it before). Anyway here is the output:

  > computation () |> run config;;
  Hello World
  val it : unit = ()

If you like you can use (>>=) instead:

let computation2 () =
    sprintf "Hello %s" <?> liftDep fst getWord
    >>= fmap (liftDep snd) print

This uses a helper function fmap that is really just function-composition or the functor-mapping for 'c -> if you want to be fancy:

let fmap (f : 'a -> 'b) 
         (g : 'c -> 'a) 
         : ('c -> 'b) =
    g >> f