Monday, March 01, 2010

Thought experiment: The Curry Facility

Please indulge me for a while, I’ve been considering how to leverage the beauty of functional programming techniques and higher order functions but with the flexibility of an IoC container.

Imagine we have a couple of components that we want to register with our IoC container:

public class ComponentOne
{
    private readonly ComponentTwo componentTwo;

    public ComponentOne(ComponentTwo componentTwo)
    {
        this.componentTwo = componentTwo;
    }

    public SomeOutput SomeOperation(string someParameter)
    {
        var input = new SomeInput { SomeParameter = someParameter };
        return componentTwo.SomeOperation(input);
    }
}

public class ComponentTwo
{
    public SomeOutput SomeOperation(SomeInput input)
    {
        return new SomeOutput {SomeOtherParameter = input.SomeParameter};
    }
}

Nothing unusual here. Now, ComponentTwo is simply a place to put the function SomeOperation, there’s no other need for it to exist. We could just as well supply a delegate to ComponentOne:

public class ComponentOne
{
    private readonly Func<SomeInput, SomeOutput> getInput;

    public ComponentOne(Func<SomeInput, SomeOutput> getInput)
    {
        this.getInput = getInput;
    }

    public SomeOutput SomeOperation(string someParameter)
    {
        var input = new SomeInput { SomeParameter = someParameter };
        return getInput(input);
    }
}

public static class FunctionsTwo
{
    public static SomeOutput SomeOtherOperation(SomeInput input)
    {
        return new SomeOutput { SomeOtherParameter = input.SomeParameter };
    }
}

FunctionsTwo is just somewhere to put SomeOperation. Everything is static so we can consider it somewhat like a module or a namespace, it serves no other purpose.

We can already register this with Windsor like so:

var container = new WindsorContainer()
    .Register(
        Component.For<ComponentOne>(),
        Component.For<Func<SomeInput, SomeOutput>>().Instance(FunctionsTwo.SomeOtherOperation)
    );

var componentOne = container.Resolve<ComponentOne>();

var output = componentOne.SomeOperation("Hello World");

// writes "Hello World" to the console
Console.WriteLine(output.SomeOtherParameter);

But what about ComponentTwo, do we really need a class for our ‘SomeOperation’ method to live in? How about if we replace it with a higher order function:

public static class FunctionsOne
{
    public static SomeOutput SomeOperation(Func<SomeInput, SomeOutput> getInput, string someParameter)
    {
        var input = new SomeInput { SomeParameter = someParameter };
        return getInput(input);
    }
}

Rather than providing the delegate as a private field of our class, that we expect to be passed as a constructor parameter, we simply provide it as the first argument of our SomeOperation method. It’s a classic example of a higher order function.

I’ve covered currying before, go and read my post on the subject if you haven’t encountered it. Languages like F# provide currying out of the box - thus F# functions only ever have one argument. You have to work a lot harder to make it work with with C#, but what if your IoC container could do the currying for you?

Now imagine we had a ‘CurryFacility’ so that we could do something like this:

var container = new WindsorContainer()
    .AddFacility<CurryFacility>()
    .Register(
        Component.For<Func<string, SomeOutput>>().SuppliedBy(FunctionsOne.SomeOperation),
        Component.For<Func<SomeInput, SomeOutput>>().SuppliedBy(FunctionsTwo.SomeOtherOperation)
    );

var getOutput = container.Resolve<Func<string, SomeOutput>>();

// writes "Hello World" to the console
Console.WriteLine(getOutput("Hello World"));

We’ve registered a service ‘Func<string, SomeOutput>’ but supplied a ‘Func<Func<SomeInput, SomeOuput>, string, SomeOutput>’. My imaginary CurryFacility would notice that to match the requested signature it would have to supply a delegate ‘Func<SomeInput, SomeOutput>’, which it would simply resolve as normal, and then partially apply SomeOperation to satisfy the service. I didn’t have time tonight to make it work, I but can’t see it being particularly painful to implement.

I’m very excited about the possibilities of functional programming, it allows us concisely describe problems without all the noise an OO language traditionally requires. In many cases a higher order function is a far more concise way of expressing the same thing as a class with DI’ed dependencies and a single method.

The CurryFacility doesn’t exist yet, but I’m going to have a go at putting something together. I’m not aware of anything like this existing already, but then I don’t follow the other .NET IoC containers. Let me know if someone’s already got an implementation.

1 comment:

  1. I've been experimenting a little myself with integrating FP and IoC a few months ago, more specifically with having the container memoize a Func. I also plan to write a tighter F#-Windsor integration (when time allows...)

    ReplyDelete

Note: only a member of this blog may post a comment.