I’ll start with examples to give you an idea of what I mean by “statically-typed heterogeneous list”. Since I’m using a stack-style API, I’ll go backwards through the term. You should know what “list” means, so I’ll use this opportunity to point out that the HList type doesn’t allow for non-empty lists. Due to the static nature of the list, it just wouldn’t make any sense for one to be empty. “Heterogeneous” means that the list can contain values of multiple types. IList<T> is a statically-typed homogeneous list since it only allows values of type T. Finally, “statically-typed” means that the compiler knows the types of the list’s members. The second example demonstrates this well; uncommenting the “sickAndTwisted” line will cause a compiler error, saving the precious puppy within from being cut in half by demented dynamic typing advocates. ArrayList is a dynamically-typed heterogeneous list; you can put values of multiple types in, but the compiler is unaware of their types.

HList shares the most similarity with the built-in (to .NET 4) Tuple type. However, HList can store an arbitrary number of values, at least until the compiler decides it has had enough of nesting generic types. I don’t know when that happens.

Without further ado:

publicstaticclassHList{publicstaticHList<T>Create<T>(Tmember){returnnewHList<T>(member);}}publicclassHList<TValue>{privatereadonlyTValue_member;publicHList(TValuemember){_member=member;}publicTValuePeek{get{return_member;}}publicHList<T,HList<TValue>>Push<T>(Tmember){// Second type argument doesn't matter, but is necessary.returnHList<T,StackOverflowException>.Create<T>(member,this);}}publicclassHList<TValue,TRest>whereTRest:class{privatereadonlyTValue_member;privatereadonlyTRest_rest;privateHList(TValuemember,TRestrest){if(rest==null){thrownewArgumentNullException("rest");}_member=member;_rest=rest;}internalstaticHList<TValue,HList<T>>Create<T>(TValuenewMember,HList<T>rest){returnnewHList<TValue,HList<T>>(newMember,rest);}publicHList<T,HList<TValue,TRest>>Push<T>(Tmember){returnnewHList<T,HList<TValue,TRest>>(member,this);}publicTRestPop(){return_rest;}publicTValuePeek{get{return_member;}}}

As you can see there is no great magic here; it’s basically the first datatype one learns in any functional language, but encoded in the type system. It’s not terribly practical, but it is pretty fun. My hope is that it’ll inspire you to play more with C♯’s type system and static type systems in general. Enjoy!