Type Classes
Type classes provide ad-hoc polymorphism in Monad, similar to Haskell type classes and Rust traits. They allow you to define interfaces that types can implement.
Basic Syntax
Define a type class with the class keyword:
class Functor (F : Type -> Type) {
def map (f : A -> B) : (F A) -> F B
}
Default Implementations
Class methods can provide default implementations using :=:
class Show A {
def show (a : A) : String := "<generic>"
}
Instances can override defaults:
instance Show I64 {
def show x := "int" // overrides the default
}
instance Show Bool {
// uses default "<generic>"
}
Classes with Constraints
Type classes can require other classes as constraints:
class [Functor F] Applicative (F : Type -> Type) {
def pure : A -> F A
def apply : F (A -> B) -> F A -> F B
}
class [Applicative M] Monad (M : Type -> Type) {
def bind (a : M A) (f : A -> M B) : M B
}
The [Functor F] syntax means "F must have a Functor instance".
Multiple Parameters
Classes can have multiple type parameters:
class HAdd A B C {
def add : A -> B -> C
}
Constraints on Classes
A class can add constraints that must be satisfied:
class [HAdd A A A] Add A {
def add : A -> A -> A
}
This means Add A requires HAdd A A A to exist.
Standard Type Classes
Functor
class Functor (F : Type -> Type) {
def map (f : A -> B) : (F A) -> F B
}
FromListLiteral
Used for list literal desugaring:
class FromListLiteral (L : Type -> Type) {
def cons (a : A) (L A) : L A
def empty : L A
}
HAdd and Add
class HAdd A B C {
def add : A -> B -> C
}
class [HAdd A A A] Add A {
def add : A -> A -> A
}
Applicative
class [Functor F] Applicative (F : Type -> Type) {
def pure : A -> F A
def apply : F (A -> B) -> F A -> F B
}
Monad
class [Applicative M] Monad (M : Type -> Type) {
def bind (a : M A) (f : A -> M B) : M B
}
From
class From T A {
def from (t : T) : A
}
HMul
class HMul A B C {
def mul : A -> B -> C
}
BEq
class BEq A {
def beq : A -> A -> Bool
}
BOrd
class BOrd A {
def cmp : A -> A -> I64
}
Show
class Show A {
def show (a : A) : String := "<generic>"
}
Sub
class Sub A {
def sub : A -> A -> A
}
Div
class Div A {
def div : A -> A -> A
}
Append
class Append A {
def append : A -> A -> A
}
DefaultValue
class DefaultValue A {
def default : A
}
Type Class Constraints
Functions can require type class instances using bracket syntax:
def process [Functor F] (f : A -> B) (fa : F A) : F B :=
Functor.map f fa
The [Functor F] syntax means "an instance of Functor for F must exist".
Infix Operators from Classes
You can create infix operators that reference class methods:
infix (>>=) := Monad.bind
infix (+) := I64.add
infix (*) := HMul.mul
Summary
- Type classes define interfaces for types
- Constraints
[C A]require instances - Classes can depend on other classes
- Instance chaining enables polymorphic behavior
Next, we'll learn about instances and how to implement type classes.