2012-10-07

True polymorphic functions in C#

If you ever saw Haskell polymorphic functions you'll never forget that great feeling of full abstraction from who may implement typeclasses that you restrict your argument/result on. Of course OOP gives opportunity to taste some bit of that feeling when we create functions which works with base types and they naturally handles all descendant, but the way they do it is run-time polymorphism and for C# that always require heap allocated objects. Sample in this post doesn't produce box/unbox even in IL while leaving structs private (and thus in maintainable state). After JIT/AOT even if code is in separate assemblies Main() from app will cause specialization of VisitAll from library which will cause specialization of PokeVisitor from app.
using System;

public interface IAnimal
{
    void Poke();
}

public class Animals
{
    private struct Cow : IAnimal
    {
        public void Poke() { Console.WriteLine("Mooo..."); }
    }

    private struct Dog : IAnimal
    {
        public void Poke() { Console.WriteLine("Bark! Bark!"); }
    }

    public interface IVisitor
    {
        void Visit<T>(T animal)
            where T : IAnimal;
    }

    public static void VisitAll<T>(T visitor)
        where T : IVisitor
    {
        visitor.Visit(default(Cow));
        visitor.Visit(default(Dog));
    }

}

public class Sample
{
    private struct PokeVisitor : Animals.IVisitor
    {
        public void Visit<T>(T animal)
            where T : IAnimal
        {
            animal.Poke();
        }
    }

    public static void Main()
    {
        Animals.VisitAll(default(PokeVisitor));
    }
}
 
Unfortunately Mono complains on generic visitor interface. Will try with Microsoft C# compiler tomorrow. In both cases there is something wrong with Mono implementation of naked type constraints.
public interface IPolymorphicFunc<TProjection>
{
    void Invoke<T>(T x) where T : TProjection;
}

public interface IAnimal
{
    void Poke();
}

public struct PokeVisitor : IPolymorphicFunc<IAnimal>
{
    public void Invoke<T>(T animal) where T : IAnimal
    //    produces: error CS0425: The constraints for type parameter `T' of method `PokeVisitor.Invoke<T>(T)' must match the constraints for type parameter `T' of interface method `IPolymorphicFunc<IAnimal>.Invoke<T>(T)'. Consider using an explicit interface implementation instead
    {
        animal.Poke();
    }

    void IPolymorphicFunc<IAnimal>.Invoke<T>(T animal)
    //    produces: error CS1061: Type `T' does not contain a definition for `Poke' and no extension method `Poke' of type `T' could be found (are you missing a using directive or an assembly reference?)
    {
        animal.Poke();
    }

    void IPolymorphicFunc<IAnimal>.Invoke<T>(T animal) where T : IAnimal
    //    produces: error CS0460: `PokeVisitor.IPolymorphicFunc<IAnimal>.Invoke<T>(T)': Cannot specify constraints for overrides and explicit interface implementation methods
    {
        animal.Poke();
    }
}