Skip to content

Latest commit

History

86 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Type Class resolution for Java

codecovMaven Central Version

Background

Type classes are a powerful abstraction mechanism popularized by Haskell.

This library implements type class resolution for Java, allowing you to define type classes and their instances (witnesses) in a modular fashion, and summon them automatically at runtime.

For a tutorial-like explanation, see: https://garciat.com/posts/java-type-classes/

Installation

The library is published on Maven Central as com.garciat.typeclasses/java-type-classes

For the core resolution model:

<dependency>
<groupId>com.garciat.typeclasses</groupId>
<artifactId>java-type-classes-core</artifactId>
<version>0.1.5</version>
</dependency>

For some predefined type classes & instances:

<dependency>
<groupId>com.garciat.typeclasses</groupId>
<artifactId>java-type-classes-instances</artifactId>
<version>0.1.5</version>
</dependency>

For the annotation processor:

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.garciat.typeclasses</groupId>
<artifactId>java-type-classes-core</artifactId>
<version>0.1.5</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

API

@interface TypeClass {
@interface Witness {}
}
interfaceTy<T> {} // witness type tokenclassTypeClasses {
static <T> Twitness(Ty<T> ty);
}

Where:

  • For a witness type C<T1, T2, ..., Tn>, witness() looks for witness constructors in C and T1, T2, ..., Tn.
  • Witness constructors for a type T are its public static methods annotated with @TypeClass.Witness.
  • For a witness constructor C<T> ctor(D1, D2, ..., Dn), the witness dependencies D1, D2, ..., Dn are resolved recursively.
  • Resolution fails when multiple witness constructors exist for a witness type, after applying overlapping instances reduction.
  • Resolution fails when a witness constructor for a witness type cannot be found.
  • Witness summoning is the result of recursively invoking witness constructors up their respective dependency trees.
  • T witness(Ty<T>) summons a witness of type T or fails with a runtime exception of type TypeClasses.WitnessResolutionException.

Example

// Type class definition@TypeClasspublicinterfaceShow<T> {
Stringshow(Tvalue);
// Helper for inference:static <T> Stringshow(Show<T> showT, Tvalue) {
returnshowT.show(value);
}
// Witness definitions:// "Leaf" witness with no dependencies:@TypeClass.WitnessstaticShow<Integer> integerShow() {
returni -> Integer.toString(i);
}
// Witness with dependencies (constraints):@TypeClass.Witnessstatic <A> Show<List<A>> listShow(Show<A> showA) {
returnlistA -> listA.stream().map(showA::show).collect(Collectors.joining(", ", "[", "]"));
}
}
// Custom typerecordPair<A, B>(Afirst, Bsecond) {
@TypeClass.Witnesspublicstatic <A, B> Show<Pair<A, B>> pairShow(Show<A> showA, Show<B> showB) {
returnpair -> "(" + showA.show(pair.first()) + ", " + showB.show(pair.second()) + ")";
}
}
// UsageclassExample {
voidmain() {
Pair<Integer, List<Integer>> value = newPair<>(1, List.of(2, 3, 4));
// Summon (and use) the Show witness for Pair<Integer, List<Integer>>:Strings = Show.show(witness(newTy<>() {}), value);
System.out.println(s); // prints: (1, [2, 3, 4])
}
}

Other features

  • Support for higher-kinded type classes like Functor<F<_>>, Monad<M<_>>, etc.
    • See the api.hkt package for details.
  • Support for overlapping instances a la Haskell.

Future work

  • Annotation processor:
    • To reify the witness graph at compile time.
    • To support parameterless witness() calls.
  • Caching of summoned witnesses.

About

Haskell-like type class resolution for Java.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Contributors

Languages