Search code examples
c#listscalacovariancecontravariance

Covariant and Contravariant Monadic Types ala Scala's List in C#


I'm new to c# but relatively experienced with scala, I'm trying to make a mimic of scala's list class (which is extended by Cons and the static class Nil). I was hoping to get the type behaviour it had as well, since .NET has supported covariance/contravariance since 4.0. Allow me to show what I mean:

Scala REPL:

class A
class B extends A
class C extends A

val x = new B() :: new B()
//this is type List[B]

val y = new C() :: new C()
//this is type List[C]

val z = new C() :: x
//This uses contravariance to figure out and infer that this is type List[A]!!!

In C# this will throw a compiler error because C and B are not the same type with ImmutableList.

There don't seem to be examples online and I'm still quite the novice with C# so I thought it would be wise to ask if C# could do this in any way before attempting blindingly (I am still trying but I'm also learning the rest of the language first as I go).

Thanks!


Solution

  • In C# this will throw a compiler error because C and B are not the same type with ImmutableList.

    In C#, classes are not co/contravariant, these are properties of Interfaces and Delegates used via the in and out keywords. Remember, in C#, a List<T> is a mutable list, and doesn't work like the immutable List[T] in Scala.

    What you can do is declare the base type for the List<T>:

    void Main()
    {
        var list = new List<A>();
        list.Add(new B());
        list.Add(new C());
    }
    
    class A { }
    class B : A { }
    class C : A { }
    

    Same goes for using an interface for T, but you can't go further than that. This will not compile:

    void Main()
    {
        var bs = new List<B>();
        var cs = new List<C>();
        var result = bs.Concat(cs);
    }
    

    For more on that, see Why isn't there generic variance for classes in C# 4.0?