Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency injection in functional programming

This problem is better illustrated with an example. I'll use Javascript (actually Coffeescript for syntax sake), but just because Javascript is just another LISP, right?

So, suppose I'm writing a web app which does (obviously) ajax requests. I implement a function to handle that:

ajaxRequest = (url, params, callback) ->
    # implementation goes here

Now, suppose I have a grid that fetches data from the server. Somewhere in my code I must do something like this:

userGrid.onMustFetch = ->
    ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
        # fill grid with data

What exactly is the problem here? If I want to test the implementation of onMustFetch, I will not be able to do so, because inside onMustFetch, a dependency is being called, and the test environment cannot control the dependency.

To solve this problem, I inject the dependency into the function I want to test. That means changing onMustFetch to this:

userGrid.onMustFetch = (ajaxRequest) ->
    ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
        # fill grid with data

Now the test code can pass a mock of ajaxRequest to onMustFetch and successfully test the behavior.

Wunderbar, right? Wrong! Now I have a second problem, the problem of having to bind the right instance of ajaxRequest to the right instance of onMustFetch.

In a language like Java, I could use a Dependency Injection framework to do this for me, and my code would look like this:

class UserGrid {

    private AjaxService ajaxService;

    @Inject
    public UserGrid(AjaxService ajaxService) {
        this.ajaxService = ajaxService;
    }

    public void onMustFetch() {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("surname", "MacGyver");
        ajaxService.request("/fetch/users", params, new AjaxCallback(data) {
            // fill grid with data
        });
    }

}

Creepy, I know... but actually the DI framework does all the wiring, so at least that part of the problem is easier.

Now back to our web app and to Javascript. Even if I successfully manage to always invoke onMustFetch with the right ajaxRequest reference (after all in this case that is not so hard to do), there must be an easier way. When my code grows, the dependencies increase. I can imagine passing a reference of ajaxRequest around, but what about when I have a securityService, a browserService, a eventBusService, etc, etc, etc.?

Now the real question here: How do lisp like languages solve this problem of managing dependencies? (It seems to me the dependencies must keep being passed around all over the application, but I'm sure there must be a better way...)

like image 869
RobotFoo Avatar asked Apr 17 '12 19:04

RobotFoo


2 Answers

This is typically done using closures/currying. You pass in your dependencies as parameters. In JS you could do:

buildUserGrid = function(dependency){
    return {
        onMustFetch = function(){
            depenency.doSomething();
        },
        doSomethingElse = function(){
            dependency.doSomethingElse();
        }
    }
}

var userGrid = buildUserGrid(ajaxRequest);
userGrid.onMustFetch();
like image 52
pondermatic Avatar answered Sep 29 '22 17:09

pondermatic


In Javascript I don't know why you couldn't use techniques similar to any OO language. A very basic implementation in JS (sorry, I don't know Coffescript)

// expects a function 
var UserGrid = function(ajaxService) {
    this.dependencies = ["ajaxService"];
     // will be overwritten by the DI service, but can also be 
     // assigned manually as in java
    this.ajaxService = ajaxService;
};
UserGrid.prototype.onMustFetch=function() {
    var callback = function() { ... }
    this.ajaxService('/fetch/users',{ surname: 'MacGyver' }, callback);
};

var diController = {
    create: function(constr) {
        var obj = new constr();
        // just look at obj.dependencies to see what should be assigned, and map
        // the implemenations as properties of obj. this could be
        // as simple as a switch or a direct mapping of names to object types
        // ... assign implementations to obj
        return obj;
    }
};

Create:

var userGrid = diController.create(UserGrid);

So diController does the same thing as your java dependency injector. In java it can just figure out what type of object is needed using reflection. There's not much reflecting to be done in Javascript, so create a convention to tell the system what is needed. In this case I used an array called "dependencies" but you could use any construct you like.

like image 27
Jamie Treworgy Avatar answered Sep 29 '22 18:09

Jamie Treworgy