public type class AdditiveMonoid<T> {
public zero() : T; // the addition identity
public add(a : T, b : T): T;
}
Lense does not allow static methods but supports type classes.
Type classes enable you to define methods that are implemented at the type class level. For example, we can define a type class for all classes that support addition.
public type class AdditiveMonoid<T> {
public zero() : T; // the addition identity
public add(a : T, b : T): T;
}
This is similar to an interface but can only be implemented at the class level, not at instance level:
public class Number satisfies AdditiveMonoid<T> {
public satisfy zero() => new Number(0);
public satisfy add(a : T, b : T) => new Number(a.value + b.value);
constructor(value : Integer);
}
The satisfies
keyword indicates that the class must comply with the type class methods. This is done by marking the methods with the satisfy
modifier. These methods will not be available in the instances of Number
but on the type itself.
let x = new Number(9);
x.add(x,x); // error, add is not available in x
x.type().add(x,x); // ok, the method is available in the type.
You can think of type classes as a contract of static methods, but that are handled like non static methods in the instance of the type.
You can access the methods in the type in 3 ways: from the instance of the class, from the name of the class and from a generic type of the class
As we have already seen, you can access the type of the class using the type()
method:
let x = new Number(9);
let doublex = x.type().add(x,x); // ok, the method is available in the instance of the type.
We can use the name of class, if it is know at compile time:
let x = new Number(9);
let doublex = typeOf(Number).add(x,x); // ok, the method is available in the instance of the type, returned by typeOf
This is ruffly the same as using static methods in other languages. For example, in java
Number x = new Number(9);
Number doublex = Number.add(x,x); // add would be a static method
The difference is that typeOf(Number)
really returns an instance of the type of number and the methods are truly polymorphic and non static.
This is a powerful concept when using generics. You can restrict the type of the class using given
in conjunction with typeOf
and due to reification, it will work.
Imagine an enhancement to sum all possible `AdditiveMonoid`s without knowing their classes:
public enhancement SumAll extends Sequence<T> given T extends AdditiveMonoid<T>{
public sum() : T {
mutable let total = typeOf(T).zero(); // the type of T is an instance of AdditiveMonoid<T>
for (let item in this){
total+= item;
}
return total;
}
}
Lense knows at runtime the true class of T and that allows the runtime to access its methods.