Some experimentation with .NET 4.0 dynamic keyword in C#.

using System;
using System.Dynamic;
using System.Linq.Expressions;
using IronScheme.Runtime;

namespace IronScheme.Dynamic
{
  static class DynamicExtensions
  {
    public static dynamic EvalDynamic(this string expr)
    {
      var r = expr.Replace("_", "-").Eval();
      if (r is Callable)
      {
        return new SchemeProcedure { Value = r };
      }
      else
      {
        return r;
      }
    }
  }

  public sealed class SchemeEnvironment : IDynamicMetaObjectProvider
  {
    public DynamicMetaObject GetMetaObject(Expression expr)
    {
      return new SchemeEnvironmentMetaObject(expr, this);
    }
  }

  class SchemeEnvironmentMetaObject : DynamicMetaObject
  {
    public SchemeEnvironmentMetaObject(Expression expr, SchemeEnvironment value)
      : base(expr, BindingRestrictions.Empty, value)
    {
    }

    public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
    {
      var expr = Expression.Call(typeof(DynamicExtensions), 
        "EvalDynamic", null, Expression.Constant(binder.Name));
      return new DynamicMetaObject(expr, 
        BindingRestrictions.GetTypeRestriction(Expression, typeof(SchemeEnvironment)));
    }
  }
  
  class SchemeProcedure : IDynamicMetaObjectProvider
  {
    public DynamicMetaObject GetMetaObject(Expression expr)
    {
      return new SchemeProcedureMetaObject(expr, Value);
    }

    public object Value { get; set; }
  }

  class SchemeProcedureMetaObject : DynamicMetaObject
  {
    public SchemeProcedureMetaObject(Expression expr, object value)
      : base(expr, BindingRestrictions.Empty, value)
    {
    }

    public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
    {
      var meth = typeof(Callable).GetMethod("Call", Array.ConvertAll(args, x => typeof(object)));
      var nargs = Array.ConvertAll(args, 
        x => Expression.Convert(
              Expression.Constant(
                x.Value is Delegate ? 
                ((Delegate)x.Value).ToSchemeProcedure() : 
                x.Value), 
              typeof(object)));
      var call = Expression.Call(Expression.Constant(Value), meth, nargs);
      return new DynamicMetaObject(call, 
        BindingRestrictions.GetTypeRestriction(Expression, typeof(SchemeProcedure)), Value);
    }
  }
}
Usage:
class Program
{
  static dynamic Scheme = new SchemeEnvironment();

  static void Main(string[] args)
  {
    var list = Scheme.list;
    var map = Scheme.map;
    var reverse = Scheme.reverse;

    Func<int, int> f = x => x * x;

    var r = map(f, reverse(map(f, list(1, 2, 3))));

    Console.WriteLine(r.car); // prints 81
  }
}

Last edited Oct 27, 2009 at 4:55 PM by leppie, version 2