Projections on IEvaluable<T, R>

In my last post I was talking about a new type called IEvaluable<T, R>, saying it is an object-oriented equivalent of a Func<T, R> delegate. In this first post scratching at the implementation of the type and its operations, I thought, I’ll start with something that is familiar to everyone using LINQ. It is the projection (or: in functional terms like F#, the map) function. Since my code here will be C#, I will call the function according to its LINQ Enumerable pendant, and this is Select. This is a motivational introduction, since I have big plans for an upcoming series of posts. They should not only cover the monadic and comonadic nature (as far as I can prove it) of the IEvaluable<T, R> type but I also attempt to perform the similar actions using F# (and as far as I can tell today, I will have to implement a computation expression in F# to achieve that). I can only do some basic F# programming and I have a little theoretical background of Haskell and I hope that I learn a lot in posting my thoughts on the IEvaluable<T, R> type. Nevertheless, I currently use this type in an ambitious start-up project as an enabler of an internal C# DSL. I am quite happy with the implementation so far, but I think there is a big chance that I can implement this kind of DSL way better using a functional language. To prove this true or false will be the quest of this and the following posts.

So, back to the initial motivation – how to project an evaluable?

Before we begin, we have to provide an implementation of IEvaluable<T, R>. A really simple yet very useful one is the following:

using System;
using dbc = System.Diagnostics.Contracts;

public class DelegateEvaluable<T, R> : IEvaluable<T, R>
{
    private readonly Func<T, R> _evaluate;

    public DelegateEvaluable(Func<T, R> evaluation)
    {
        dbc.Contract.Requires<ArgumentNullException>(evaluation != null, "evaluation must not be null");
        _evaluate = evaluation;
    }

    public R Evaluate(T param)
    {
        return _evaluate(param);
    }
}

All it does, is wrapping a Func<T, R> delegate that will be executed whenever its Evaluate method is called. If you are familiar with WPF development, this looks very similar to a DelegateCommand. When it comes to using it, it almost feels like its delegate pendant:

var e = new DelegateEvaluable<int, string>(i => i.ToString());
Func<int, string> f = i => i.ToString();

Assert.True(e.Evaluate(5) == f(5));

Implementing a projection on both can also be quite similar, but we will see, that there is an alternative approach for the evaluable as well. A projection for a function Func<T, R> is a function Func<R, S> that maps the result of the first function (which is of type R) to another type S. This is just basic function composition.

Func<int, string> f = i => i.ToString();
Func<string, IEnumerable<char>> map = s => s.AsEnumerable();

var chars = map(f(12345));

Although we could do it in a similar way for the evaluable, let’s introduce a new class that can do that. I’ll call it the MappingEvaluable.

public class MappingEvaluable<TCandidate, TInputResult, TOutputResult> : IEvaluable<TCandidate, TOutputResult>
{
    private readonly Func<TInputResult, TOutputResult> _mapping;
    private readonly IEvaluable<TCandidate, TInputResult> _evaluableToMap;

    public MappingEvaluable(IEvaluable<TCandidate, TInputResult> evaluableToMap, Func<TInputResult, TOutputResult> mapping)
    {
        dbc.Contract.Requires<ArgumentNullException>(evaluableToMap != null, "evaluableToMap must not be null");
        dbc.Contract.Requires<ArgumentNullException>(mapping != null, "mapping must not be null");

        _evaluableToMap = evaluableToMap;
        _mapping = mapping;
    }

    public TOutputResult Evaluate(TCandidate candidate)
    {
        return _mapping(_evaluableToMap.Evaluate(candidate));
    }
}

Since the evalubale version is more characters to write and the delegate version is identical in behavior, one might ask, why you should use the evaluable instead. Though this is something I will begin to explain in the next post, here is a first impression: you can add context to the wrapped function using the evaluable. One might argue that this is possible with functions also – by making use of closures. That’s certainly right. Since I am still not sure what is the right way to go I’ll explore the different options and learn along the path.

In the next post I will take a step back from the implementation and provide some thoughts on the general nature of the IEvaluable<T, R> type.

Leave a Reply

Your email address will not be published. Required fields are marked *