Functionally dynamic?
I was just playing with this tonight, but I don’t know it’s worth anything. I thought of it after some conversations with Matt Podwysocki back at KaizenConf on how do apply some functional ideas in C#. First, I started with a simple class:
public class OriginalProduct { public decimal Price { get; set; } public decimal Cost { get; set; } public decimal CalculateProfit() { return Price - Cost; } }
To do anything interesting, functional-wise, like swapping implementations or doing things like decorators is annoying or impossible. But if I went this (albeit weird) direction:
public class Product { public decimal Price { get; set; } public decimal Cost { get; set; } public Product() { CalculateProfit = calculateProfit; } public Func<decimal> CalculateProfit { get; set; } private decimal calculateProfit() { Console.WriteLine("Calculating profit"); return Cost == 0 ? 0m : Price - Cost; } }
Yes, weird. But tests look rather normal, except a function is a property:
[Test] public void No_change() { var product = new Product {Price = 11, Cost = 10}; product.CalculateProfit().ShouldEqual(1m); }
Because you can invoke delegate instances directly, it looks like I’m calling a function on the type. I can do some interesting things with some simple decorator extensions:
public static class DelegateExtensions { public static Func<T> Before<T>(this Func<T> func, Action before) { return () => { before(); return func(); }; } public static Func<T> After<T>(this Func<T> func, Action after) { return () => { T value = func(); after(); return value; }; } }
And now I can decorate my the functions at will:
[Test] public void Decorator_fun() { var product = new Product { Price = 11, Cost = 10 }; product.CalculateProfit = product.CalculateProfit .Before(() => Console.WriteLine("Before")) .After(() => Console.Write("After")); product.CalculateProfit().ShouldEqual(1m); }
This outputs:
Before Calculating profit After 1 passed, 0 failed, 0 skipped, took 1.05 seconds.
That’s just some simple decorator implementation. Finally, I can swap out implementations at runtime:
[Test] public void Straight_up_replacement() { var product = new Product { Price = 11, Cost = 10 }; product.CalculateProfit = () => 1500; product.CalculateProfit().ShouldEqual(1500m); }
It’s still not dynamic typing, as I can’t add new members at runtime. I have no idea if this is useful or not, as the structure is rather weird, and you’d need to do some interesting things to get polymorphism in play. But, it might have some interesting applications in some strange scenario.