About Jolt.NET Libraries

Inspired by the Boost C++ libraries, Jolt.NET aims to complement the .NET Base Class Library (BCL) with algorithms, data structures, and general productivity tools. It is the hope of the authors that the features of Jolt.NET will one day be part of, or represented in the BCL and the .NET Framework.

Static Idempotent Functors

The task of replacing inline anonymous methods with a generic functor from the Jolt.Functional library brought forth some interesting issues, all of which have been addressed as of revision #19483.  The main topic that I will discuss in this post deals with the implementation and usage of the Functor.Idempotency and Functor.TrueForAll generic factory methods.

The TrueForAll factory method creates a delegate that returns true for any input, and thus it is natural to implement this method in terms of the Functor.Idempotency method since TrueForAll is a Boolean idempotent predicate.  My main use of TrueForAll in Jolt.NET was in testing the serialization of finite automata to GraphML.  If you’ve used this library, you may recall that there are limitations when serializing of a TransitionPredicate.  Specifically, the predicate must refer to a static method.  So, is it possible to implement a generic idempotent method that is static?  Let’s take a look at the function signature that we need to satisfy, along with the current implementation.

using System;

// One-arg idempotent method; overloads exist for zero through four args.
static Func<T, TResult> Idempotency<T, TResult>(TResult constant)
{
return arg => constant;
}

In this example, the method that ultimately implements the inline expression can not be static since it uses a variable that is not local to the expression (see “Anonymous Methods and Jolt Functors: Behind the Scenes” for more information).  Consequently, the resulting delegate refers to a non-static method.  If the hidden class that implements the expression were static, then we could only ever have one idempotent delegate per unique specialization. per app-domain, as demonstrated in the following example.


using System;

static class IdemptotencyWrapper<TConstant>
{
static TConstant constant;
static TConstant Idempotency<T>(T arg)
{
return constant;
}
}

static Func<T, TResult> Idempotency<T, TResult>(TResult constant)
{
IdempotencyWrapper<TResult>.constant = constant;
return IdempotencyWrapper<TResult>.Idempotency<T>;
}

void UseFunctors()
{
Func<int, bool> falseForAll = Idempotency<int>(false);
Func<int, bool> trueForAll = Idempotency<int>(true); // Oops! Changed the behavior of falseForAll.
}

Another novel attempt at solving this problem is to use an expression tree to force the user-provided constant to be local to the expression.

using System;

static Func<T, TResult> Idempotency<T, TResult>(TResult constant)
{
Expression<Func<T, TResult>> expression = Expression.Lambda(
Expression.Constant(constant, typeof(TResult)),
new[] { Expression.Parameter(typeof(T), arg) });
return expression.Compile();
}

This solution looks good, has some performance deficiencies we can live with, and may even provide the static method that we need.  However, the method that is produced is compiled and created at runtime.  In other words, it doesn’t exist in the assembly and thus not feasible for textual serialization.

I’m also uncertain on whether this method is truly static.  The MethodInfo object representing the delegate’s method states that IsStatic is true, but the delegate’s Target property gives a non-null value.  Very strange.

If we want to persist the method that is represented by the expression, we can utilize the Reflection.Emit API and create an assembly that contains the desired method.  The performance deficiency is even greater with this solution and is overkill for the general usage of the Idempotency method.  While the serialization problem is addressed, the deserialization problem is not; this generated assembly is not distributed with the library and thus the referenced methods may not exist in deserialization environment.

In conclusion, I could not conceive of a way to modify the Idempotency method implementation to meet my criteria, so I decided to keep it as is.  The implementations of TrueForAll and FalseForAll were modified to return constants local to the expressions, thus making the resulting delegate refer to a static method.

0 comments: