Skip to content

Generics vs Templates

Andrew Johnson edited this page Jul 4, 2024 · 6 revisions

There are two similar features that LM Supports that initially may seem very similar to the end user but in implementation they are quite different.

What is an Open Type?

An open type is any type that contains a type variable such as List<a>. This is the type of a generic list that hasn't been given a concrete parameter yet. When processing open types there are two philosophically distinct approaches to semantic code generation.

Generics are Type Erasure

Languages such as Rust or Java take the generic approach to open types. Upon encountering a generic parameter, that type parameter will be weakened down to its minimal parameterized bound. To practically use generic type parameters, each type must be given constraints that specify what behavior will be available.

For example List<a> might not be very useful because it will erase down to the bottom type List<?> which means that we know precisely nothing about the parameter and probably can't do anything useful with it. We don't even know the size of this type.

However, by adding constraints we can specify how much information that we need about the type to use it. Now if we add more information such as List<PartialOrd> we can create a generic list for things that can be ordered. This list may now allow us to sort the elements.

Templates are Code Generation

The other major option to dealing with parameterized types is called templating and is used by languages such as C++ or Zig. Upon encountering a parameterized type in a function, a new copy of that function will be created with the function body becoming specialized to the new concrete type. Sometimes it is difficult to create a truly generic implementation of a parameterized function, so substitution becomes very handy.

For example, consider the templated is operator in the standard library. The is comparison operator will return True if and only if two pieces of data are literally identical. This means that they are the same size and are exactly the same binary contents. When applying this function a parameter of Sized<24> will create a new copy of the function if it does not already exist. This new copy will specialize all constants that reference that type parameter or use ascription or type coercion. For the is function this means creating a for loop that iterates over and compares the contents of two values. The length parameter of the iterator needs to be specialized for this to work.