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

functor

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

applicative-functor

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

monad

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

Example

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
  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1817()

  • Pingback: Dew Drop – March 11, 2015 (#1972) | Morning Dew()

  • Just to let you know this page doesn’t load on mobile (chrome on android Lollipop). The page simply stops loading past “Another solution that can work is to make the dependency a mutable part of your defining module:”

    • Thanks – this is really strange – to be honest I kindof hoped that WordPress would handle this stuff for me (Web/CSS/Javascript … doh) – and of course *it works on my devices* 🙁 … I have to get a hold on one of those somehow.

      Can someone give me a hint of how to check this?

      • I’ll take a look at debugging it later on today, see what’s up with it

  • Pingback: F# Weekly #11, 2015 | Sergey Tihon's Blog()

  • Ruxo Zheng

    Your article is really useful. Thank you 🙂

  • Thank you, this was very useful post! Helped me to understand the Reader monad.

    Is it just me or are the arguments of the bind function in wrong order?

    • oops … yeah you are right thanks for the hint (I hope I did not mess up again – no clue how this happened here)

      • Sorry, I think I wasn’t clear enough. In code example you define bind m f, but then when introducing >>= operator you call it (bind f m). So, it seems to me that you like the value first order so much that you flipped it (by mistake) already when introducing the bind function? 🙂

        I admit that I’m very novice and might be just plain wrong here. 🙂

        • nah you are right – I should have checked – and I hope it’s correct now ^^ – thanks — I probably had the let bind ... flipped – probably to show the similarity to fmap or something but decided later to unify it

          • Yeah, works great now. I was just playing around with the code and got a bit confused when things just didn’t make sense. It was just a minor typo in otherwise really helpful and clean post.

  • Ody Mbegbu

    I got lost at “the fancy solution”