Type classes
A type class is a special pseudo-type that can be used to match against types in the context of overload resolution or the is operator. Nim supports the following built-in type classes:
type class | matches |
---|---|
object | any object type |
tuple | any tuple type |
enum | any enumeration |
proc | any proc type |
iterator | any iterator type |
ref | any ref type |
ptr | any ptr type |
var | any var type |
distinct | any distinct type |
array | any array type |
set | any set type |
seq | any seq type |
auto | any type |
Furthermore, every generic type automatically creates a type class of the same name that will match any instantiation of the generic type.
Type classes can be combined using the standard boolean operators to form more complex type classes:
# create a type class that will match all tuple and object types
type RecordType = (tuple or object)
proc printFields[T: RecordType](rec: T) =
for key, value in fieldPairs(rec):
echo key, " = ", value
Type constraints on generic parameters can be grouped with , and propagation stops with ;, similarly to parameters for macros and templates:
proc fn1[T; U, V: SomeFloat]() = discard # T is unconstrained
template fn2(t; u, v: SomeFloat) = discard # t is unconstrained
Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data types in ML-like languages, it should be understood that type classes are static constraints to be enforced at type instantiations. Type classes are not really types in themselves but are instead a system of providing generic “checks” that ultimately resolve to some singular type. Type classes do not allow for runtime type dynamism, unlike object variants or methods.
As an example, the following would not compile:
type TypeClass = int | string
var foo: TypeClass = 2 # foo's type is resolved to an int here
foo = "this will fail" # error here, because foo is an int
Nim allows for type classes and regular types to be specified as type constraints of the generic type parameter:
proc onlyIntOrString[T: int|string](x, y: T) = discard
onlyIntOrString(450, 616) # valid
onlyIntOrString(5.0, 0.0) # type mismatch
onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
proc and iterator type classes also accept a calling convention pragma to restrict the calling convention of the matching proc or iterator type.
proc onlyClosure[T: proc {.closure.}](x: T) = discard
onlyClosure(proc() = echo "hello") # valid
proc foo() {.nimcall.} = discard
onlyClosure(foo) # type mismatch