Type-safe solution to the expression problem in C#?

Posted this on Channel9 (http://channel9.msdn.com/ShowPost.aspx?PostID=386812), but didn't get much feedback, so I thought I'd repost here.

Basically, I've been looking at a type-safe approach for the expression problem in C#, taking advantage of the lambda expression runtime compilation command Expression.Compile() in C#'s Language Integrated Query (LINQ). The biggest drawback to the solution is that the compiled lambda functions take about 2x longer to execute on primitive expressions compared to non-generic solutions. I haven't yet found an EvalStream implementation that can avoid this overhead of lambda evaluation. I'm pretty new to functional programming, but I'd love to hear anyone's thoughts on the approach I'm trying here (see LinFu author Philip Laureano's comments (thanks Philip!): http://plaureano.blogspot.com/2008/02/linfudynamicobject-and-dynamic-object.html#c4431642200009960939)

using System;
using System.Linq.Expressions;

namespace TPLTest
{
public class TestGADT
{
public static void Main()
{
Number n1 = 5;
Number n2 = 4;
Console.WriteLine(n1 + n2);

Array array1 = new Array(1000).InitializeTo(10);
Array array2 = new Array(1000).InitializeTo(20);
Array array3 = array1 + array2;
Console.WriteLine(array3[0]);

Console.Read();
}
}

public struct Number where T : struct
{
private T numValue;
public static Number operator +(Number a, Number b) { return GADT.AddFunc(a.numValue, b.numValue); }
public static Number operator -(Number a, Number b) { return GADT.SubtractFunc(a.numValue, b.numValue); }
public static Number operator *(Number a, Number b) { return GADT.MultiplyFunc(a.numValue, b.numValue); }
public static Number operator /(Number a, Number b) { return GADT.DivideFunc(a.numValue, b.numValue); }

public static implicit operator Number(T myValue) { return new Number() { numValue = myValue }; }
public static implicit operator T(Number myNumber) { return myNumber.numValue; }
public override string ToString() { return numValue.ToString(); }
public T ToValue() { return numValue; }
}

public class Array where T : struct
{
internal T[] arrayInstance;

public Array() { }
public Array(int size) { arrayInstance = new T[size]; }
public Array(T[] existingArrayInstance) { arrayInstance = existingArrayInstance; }
public T this[int index] { get { return arrayInstance[index]; } set { arrayInstance[index] = value; } }
public int Length { get { return arrayInstance.Length; } }

public static implicit operator T[](Array myArray) { return myArray.arrayInstance; }
public static implicit operator Array(T[] myArrayInstance) { return new Array() { arrayInstance = myArrayInstance }; }

public static Array operator +(Array a, Array b) { return GADT.AddFunc.EvalStream(a, b); }
public static Array operator -(Array a, Array b) { return GADT.SubtractFunc.EvalStream(a, b); }
public static Array operator *(Array a, Array b) { return GADT.MultiplyFunc.EvalStream(a, b); }
public static Array operator /(Array a, Array b) { return GADT.DivideFunc.EvalStream(a, b); }

public T[] ToArray() { return arrayInstance; }
}

public static class ArrayExtensions
{
public static Array EvalStream(this Func myFunc, Array a, Array b) where T : struct
{
Array output = new Array { arrayInstance = new T[a.arrayInstance.Length] };
for (int i = 0; i output.arrayInstance[i] = myFunc(a.arrayInstance[i], b.arrayInstance[i]);
return output;
}
public static Array InitializeTo(this Array myArray, T value) where T : struct
{
for (int i = 0; i myArray[i] = value;
return myArray;
}
}

public static class GADT
{
public static Func AddFunc = typeof(GADTHelper).GetFunc("Add");
public static Func SubtractFunc = typeof(GADTHelper).GetFunc("Subtract");
public static Func MultiplyFunc = typeof(GADTHelper).GetFunc("Multiply");
public static Func DivideFunc = typeof(GADTHelper).GetFunc("Divide");

internal static class GADTHelper
{
public static double Add(double a, double b) { return a + b; }
public static float Add(float a, float b) { return a + b; }
public static int Add(int a, int b) { return a + b; }
public static long Add(long a, long b) { return a + b; }

public static double Subtract(double a, double b) { return a - b; }
public static float Subtract(float a, float b) { return a - b; }
public static int Subtract(int a, int b) { return a - b; }
public static long Subtract(long a, long b) { return a - b; }

public static double Multiply(double a, double b) { return a * b; }
public static float Multiply(float a, float b) { return a * b; }
public static int Multiply(int a, int b) { return a * b; }
public static long Multiply(long a, long b) { return a * b; }

public static double Divide(double a, double b) { return a / b; }
public static float Divide(float a, float b) { return a / b; }
public static int Divide(int a, int b) { return a / b; }
public static long Divide(long a, long b) { return a / b; }
}
}

public static class DynamicExtensions
{
public static Func GetFunc(this Type t, string method)
{
Expression body = Expression.Call(t.GetMethod(method));
Expression e = Expression.Lambda(body);
return e.Compile();
}
public static Func GetFunc(this Type t, string method)
{
Type[] types = new Type[] { typeof(T) };
var pArray = types.GetParameterExpressionArray();
Expression body = Expression.Call(t.GetMethod(method, types), pArray);
Expression e = Expression.Lambda(body, pArray);
return e.Compile();
}
public static Func GetFunc(this Type t, string method)
{
Type[] types = new Type[] { typeof(T1), typeof(T2) };
var pArray = types.GetParameterExpressionArray();
Expression body = Expression.Call(t.GetMethod(method, types), pArray);
Expression e = Expression.Lambda(body, pArray);
return e.Compile();
}
public static Func GetFunc(this Type t, string method)
{
Type[] types = new Type[] { typeof(T1), typeof(T2), typeof(T3) };
var pArray = types.GetParameterExpressionArray();
Expression body = Expression.Call(t.GetMethod(method, types), pArray);
Expression e = Expression.Lambda(body, pArray);
return e.Compile();
}
public static Func GetFunc(this Type t, string method)
{
Type[] types = new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) };
var pArray = types.GetParameterExpressionArray();
Expression body = Expression.Call(t.GetMethod(method, types), pArray);
Expression e = Expression.Lambda(body, pArray);
return e.Compile();
}
private static ParameterExpression[] GetParameterExpressionArray(this Type[] types)
{
ParameterExpression[] pArray = new ParameterExpression[types.Length];
for (int i = 0; i pArray[i] = Expression.Parameter(types[i], "p" + i);
return pArray;
}
}
}

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

[Admin]

Please read the LtU policies (regarding design discussions and user names, in particular) and try to adhere to them.

Thanks.

Expression Problem Solved in C#

Just make a quick note: I've already posted a solution to the expression problem in C#, and it doesn't involve runtime compilation, just pure C# generics. I link to the paper which first proposed this solution as well. It's interesting reading.