DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. A Functional IoC Container

A Functional IoC Container

Mike Hadlow user avatar by
Mike Hadlow
·
Oct. 15, 12 · Interview
Like (0)
Save
Tweet
Share
3.73K Views

Join the DZone community and get the full member experience.

Join For Free

Today I was idly thinking about an idea I had a couple of years ago for a functional IoC container. I’d had a go at implementing such a beast, but soon got bogged down in a tangled mess of spaghetti reflection code and gave it up as too much bother. But today it suddenly occurred  to me that there was no need for any reflection voodoo; the C# type system is powerful enough to do all the work for us.

In object oriented programming languages we build programs from classes. Classes declare the contract(s) they support with interfaces and declare their dependencies with constructor arguments. We use an IoC container to wire instances of our classes together to make a running program.

Pure functional languages, like Haskell, don’t have any concept of class, instead they use currying and partial application to compose hierarchies of functions.

Here’s an example of a purely functional program written in C#.

public static class Module
{
    public static Data GetAndTransform(Func<Input,Data> dataAccsessor, Func<Data,Data> transformer, int id)
    {
        var input = new Input() {Id = id};
        var data = dataAccsessor(input);
        var transformed = transformer(data);
        return transformed;
    }

    public static Data DataAccsessor(Input input)
    {
        return new Data
        {
            Id = input.Id,
            Name = "Test"
        };
    }

    public static Data Transformer(Data original)
    {
        original.Name = original.Name + " transformed";
        return original;
    }
}

GetAndTransform simply takes an int id argument, does some work, and then returns some data. It needs a dataAccsessor and a transformer in order to do its job.

C# doesn’t support currying or partial application, so in order to run it we have to compose the program and execute it all in one step. For example:

var id = 10;
var data = Module.GetAndTransform(Module.DataAccsessor, Module.Transformer, id);

Console.Out.WriteLine("data.Id = {0}", data.Id);
Console.Out.WriteLine("data.Name = {0}", data.Name);

But what if we had a ‘currying container’, one that could compose the program in one step and then return a function for us to execute in another? Here is such a container at work with our program:

var registration = new Container()
    .Register<Func<Input, Data>, Func<Data, Data>, int, Data>(Module.GetAndTransform)
    .Register<Input,Data>(Module.DataAccsessor)
    .Register<Data,Data>(Module.Transformer);

var main = registration.Get<Func<int, Data>>();

var data = main(10);

Console.Out.WriteLine("data.Id = {0}", data.Id);
Console.Out.WriteLine("data.Name = {0}", data.Name);
In the first line, we create a new instance of our container. On the next three lines we register our functions. Unfortunately C#’s type inference isn’t powerful enough to let us do away with the tedious type annotations; we have to explicitly declare the argument and return types of each of our functions.

Once our functions are registered we can ask the container for a program (main) that takes an int and returns a Data instance. The container works out that it needs to curry GetAndTransform and then partially apply DataAccsessor and Transformer to it to produce the desired function.

We can then run our ‘main’ function which gives us our expected output:

data.Id = 10
data.Name = Test transformed

The container turns out to be very simple, just a dictionary that’s keyed by type and contains a collection of constructor functions that know how to build the target (key) type.

 

public interface IRegistration
{
    void Add(Type target, Func<object> constructor);
    T Get<T>();
}

public class Container : IRegistration
{
    private readonly Dictionary<Type, Func<object>> registrations = new Dictionary<Type, Func<object>>();

    public void Add(Type target,  Func<object> constructor)
    {
        registrations.Add(target, constructor);
    }

    public T Get<T>()
    {
        return (T)registrations[typeof (T)]();
    }
}

The magic sauce is in the Registration function overloads. If you take the standard functional idea that a function should only have one argument and one return type, you can take any input function, curry it, and then partially apply arguments until you are left with a Func<X,Y>. So you know what the ‘target’ type of each function should be, a function from the last argument to the return type. A Func<A,B,C,R> gets resolved to a Func<C,R>. There’s no need to explicitly register a target, it’s implicit from the type of the provided function:

public static class RegistrationExtensions
{
    public static IRegistration Register<A,R>(this IRegistration registration, Func<A, R> source)
    {
        var targetType = typeof (Func<A, R>);
        var curried = Functional.Curry(source);

        registration.Add(targetType, () => curried);

        return registration;
    }

    public static IRegistration Register<A,B,R>(this IRegistration registration, Func<A, B, R> source)
    {
        var targetType = typeof (Func<B, R>);
        var curried = Functional.Curry(source);

        registration.Add(targetType, () => curried(
            registration.Get<A>()
            ));

        return registration;
    }

    public static IRegistration Register<A, B, C, R>(this IRegistration registration, Func<A, B, C, R> source)
    {
        var targetType = typeof(Func<C, R>);
        var curried = Functional.Curry(source);

        registration.Add(targetType, () => curried(
            registration.Get<A>()
            )
            (
            registration.Get<B>()
            ));

        return registration;
    }
}
 
Each overload deals with an input function with a different number of arguments. My simple experiment only works with functions with up to three arguments (two dependencies and an input type), but it would be easy to extend for higher numbers. The Curry function is stolen from Oliver Sturm and looks like this:
 
public static class Functional
{
    public static Func<A, R> Curry<A, R>(Func<A, R> input)
    {
        return input;
    }

    public static Func<A, Func<B, R>> Curry<A, B, R>(Func<A, B, R> input)
    {
        return a => b => input(a, b);
    }

    public static Func<A, Func<B, Func<C,R>>> Curry<A, B, C, R>(Func<A, B, C, R> input)
    {
        return a => b => c => input(a, b, c);
    }
}
 
Rather nice, even if I say so myself.

Of course this little experiment has many limitations. For a start it only understands functions in terms of Func< … >, so you can’t have more than one function of each ‘type’. You couldn’t have two Func<int,int> for example, which might be somewhat limiting.

The code is on GitHub here if you want to have a play.

Container

Published at DZone with permission of Mike Hadlow, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Java Development Trends 2023
  • 13 Code Quality Metrics That You Must Track
  • Deploying Java Serverless Functions as AWS Lambda
  • Automated Performance Testing With ArgoCD and Iter8

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: