{"article":{"slug":"full-haskell-like-type-class-resolution-in-java","title":"Full Haskell-like Type Class resolution in Java","subtitle":null,"summary":"Gabriel Garcia re-implements Haskell-style type classes in pure Java: using reflection and generics to parse static types, unify them, and automatically resolve instance witnesses, then extends the approach to higher-kinded types, context instances and overlapping instances, with worked examples like Show, Monoid, Traversable and Monad.","content_type":"tutorial","language":"en","canonical_url":"https://garciat.com/posts/java-type-classes/","author":{"name":"Gabriel Garcia","url":"https://garciat.com/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"garciat.com","url":"https://garciat.com/","listing_slug":null,"listing":null},"topics":[{"name":"Programming Languages","slug":"programming-languages","url":"https://listedarticles.com/topics/programming-languages"},{"name":"Java","slug":"java","url":"https://listedarticles.com/topics/java"},{"name":"Haskell","slug":"haskell","url":"https://listedarticles.com/topics/haskell"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":7285,"reading_minutes":32,"published_at":"2025-12-06T00:00:00.000Z","added_at":"2026-10-27T11:12:36.530Z","updated_at":"2026-10-27T11:12:36.530Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/full-haskell-like-type-class-resolution-in-java","markdown_url":"https://listedarticles.com/articles/full-haskell-like-type-class-resolution-in-java.md","example":false,"citation":"Gabriel Garcia, garciat.com. \"Full Haskell-like Type Class resolution in Java.\" 6 Dec 2025. https://garciat.com/posts/java-type-classes/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://garciat.com/posts/java-type-classes/"},"body_markdown":"# Full Haskell-like Type Class resolution in Java\n\n*Gabriel Garcia · December 6, 2025*\n\n## TL;DR\n\nThis post explores how far Java’s type system can be pushed by building a small\nHaskell-style type class instance resolution engine entirely in plain Java.\n\nUsing only reflection, generic type metadata, and a tiny first-order unifier, we\ncan automatically resolve type class instances (including higher-kinded ones),\nsupport overlapping instances, and derive witnesses for arbitrarily nested\ngeneric types.\n\nIt’s not intended for production, but as an experiment it reveals just how much\nstructure Java actually preserves at runtime — and how surprisingly close it can\nget to type-level programming without language changes.\n\nYou can also jump straight to the full implemention\n[in this gist](https://gist.github.com/Garciat/204226a528018fa7d10abb93fa51c4ca).\n\nUpdate: I published a GitHub repo for the project at\n[github.com/garciat/java-type-classes](https://github.com/garciat/java-type-classes).\n\n## Intro: What are Type Classes?\n\nConsider:\n\n```\ninterface Equatable<T> {\n  boolean eq(T other);\n}\n\nrecord Point(int x, int y) implements Equatable<Point> {\n  @Override\n  public boolean eq(Point other) {\n    return x == other.x() && y == other.y();\n  }\n}\n```\n\nThat's a pretty simple OOP-style generic interface.\n\nHowever:\n\n```\nrecord Dup<A>(A fst, A snd) implements Equatable<Dup<A>> {\n  @Override\n  public boolean eq(Dup<A> other) {\n    return fst.eq(other.fst()) && snd.eq(other.snd());\n    // Error: no method eq() in type A!\n  }\n}\n```\n\nWe can't implement `eq()` for `Dup` without knowing the fact that\n`A extends Equatable<A>`.\n\nSo let's try that:\n\n```\nrecord Dup<A extends Equatable<A>>(A fst, A snd) implements Equatable<Dup<A>> {\n  @Override\n  public boolean eq(Dup<A> other) {\n    return fst.eq(other.fst()) && snd.eq(other.snd());\n    // OK\n  }\n}\n```\n\nNow the code compiles.\n\nHowever, `Dup` is now only compatible with types that extend `Equatable`. `Dup`\nis now less capable.\n\nWe could have checked at runtime if each object is `instanceof Equatable`, but\nthat doesn't work very well with generics. And it may fail at runtime.\n\nIdeally, there would be a way to say:\n\n`Dup` implements `Equatable<Dup<A>>` **if and only if** `A` implements\n`Equatable<A>`.\n\n> Also, if you think about it, it is a bit strange that the definition of\n> equality for a type `A` depends on the object instance. As in, `eq()` is an\n> instance method.\n>\n> It would make more sense if equality depended only on the type `A` itself.\n> Right?\n\nThat's where type clases come in:\n\n```\ninterface Eq<A> {\n  boolean eq(A x, A y);\n}\n\nrecord Point(int x, int y) {\n  public static Eq<Point> eq() {\n    return (p1, p2) ->\n        p1.x() == p2.x()\n        && p1.y() == p2.y();\n  }\n}\n\nrecord Dup<A>(A fst, A snd) {\n  public static <A> Eq<Dup<A>> eq(Eq<A> eqA) {\n    return (d1, d2) ->\n        eqA.eq(d1.fst(), d2.fst())\n        && eqA.eq(d1.snd(), d2.snd());\n  }\n}\n```\n\nLet's unpack this:\n\n- `Eq<A>` now compares two values of type `A`.\n  - This indicates that we are no longer expecting the implicit `this` parameter\n    to be relevant in equality.\n- `Point` no longer implements the interface.\n  - Now it provides a static constructor (factory) for `Eq<Point>`.\n  - We call this a *witness* that `Point` conforms to `Eq`.\n- `Dup` also does not implement the interface.\n  - Its static constructor for `Eq<Dup<A>>` has a parameter of type `Eq<A>`.\n    - This indicates the dependency that we were looking for:\n    - In order to prove `Eq<Dup<A>>`, we must first prove `Eq<A>`.\n  - It uses the witness `Eq<A> eqA`, which contains `boolean eq(A, A)`, to\n    implement its own intended behavior.\n\nThe code is rather simple, but it marks a dramatic change of perspective:\n\nEquality now belongs to the type definition and not to an object instance.\n\nSo, what are type classes?\n\nType classes can be seen as a pattern that allows us to model shared behavior in\na way that puts types first AND is entirely compositional.\n\nNow, in the above example, how do we actually use `Dup`'s `eq()`?\n\n```\nEq<Dup<Point>> pointDupEq = Dup.eq(Point.eq());\n\npointDupEq.eq(new Point(1, 1), new Point(1, 1));\n// Returns: true\n```\n\nWe must first construct the witness `Eq<Dup<Point>>` by chaining calls to the\nrespective `Eq` witness constructors of `Dup` and `Point`.\n\nThen, we use this witness to access/invoke the actual logic that we're after.\n\nNotice how this pattern composes very nicely:\n\n```\nstatic <K, V> Eq<Map<K, V>> mapEq(Eq<K> eqK, Eq<V> eqV) { ... }\n\nstatic <E> Eq<List<E>> listEq(Eq<E> eqE);\n\nstatic Eq<Integer> integerEq();\n\nstatic Eq<String> stringEq();\n\n// then:\n\nEq<Map<String, List<Integer>>> eq =\n  mapEq(stringEq(), listEq(integerEq()));\n\nMap<String, List<Integer>> m1 = ...;\nMap<String, List<Integer>> m2 = ...;\n\neq.eq(m1, m2);\n```\n\nWith witness constructors as our building blocks, we can recursively construct\ninfinitely many witnesses, as needed.\n\n> A note on nomenclature:\n>\n> In Haskell, type class 'implementations' are called *instances*.\n>\n> Here, I am calling them *witnesses* because that's how Java's Architect, Brian\n> Goetz, decided to refer to them in\n> [a recent talk](https://www.youtube.com/watch?v=Gz7Or9C0TpM) he gave about\n> bringing type classes to Java sometime in the future.\n\nNow, wouldn't it be great if we didn't have to manually construct these\nwitnesses?\n\n## Goal: Programmatically instantiating witnesses\n\nGiven:\n\n```\n@TypeClass\ninterface Show<A> {\n  String show(A value);\n\n  // Convenience shortcut\n  static <A> String show(Show<A> showA, A value) {\n    return showA.show(value);\n  }\n\n  @TypeClass.Witness\n  static Show<Integer> showInteger() { ... }\n\n  @TypeClass.Witness\n  static <A> Show<List<A>> showList(Show<A> showA) { ... }\n}\n```\n\nInstead of:\n\n```\nShow<List<List<Integer>>> w =\n    Show.showList(Show.showList(Show.showInteger()));\n\nShow.show(w, List.of(List.of(1, 2), List.of(3, 4)));\n// Returns: \"[[1, 2], [3, 4]]\"\n```\n\nWe want:\n\n```\nShow.show(witness(), List.of(List.of(1, 2), List.of(3, 4)));\n// Returns: \"[[1, 2], [3, 4]]\"\n```\n\nWhich means that the `witness()` method is able to automatically construct the\nrequired witness for `Show<List<List<Integer>>>`.\n\nHow do we get there?\n\nFirst, we must understand some aspects of how Java generics work at runtime.\n\n## Aside: Java Generics and Type Erasure\n\nIt is a well-known fact that Java generics get erased at runtime.\n\nHowever, there are a few scenarios in which generics *do* get reified!\n\n> Note: 'Type Reification' is the process by which type information is made\n> concrete and available at runtime.\n\nGiven:\n\n```\ninterface Stream<T> { ... }\n\ninterface IntStream extends Stream<Integer> { ... }\n```\n\nThen:\n\n```\nIntStream.class.getInterfaces()[0];\n// Returns: Stream\n\nIntStream.class.getGenericInterfaces()[0];\n// Returns: Stream<Integer>\n```\n\nThis means that Java *did* preserve the generic type arguments for the supertype\n`Stream<Integer>`. (We just need to know where to look for it.)\n\nNote that this also occurs with anonymous classes:\n\n```\nvar s = new Stream<String>() { ... };\n\ns.getClass().getGenericInterfaces()[0];\n// Returns: Stream<String>\n```\n\nThis leads us to our first workaround:\n\n## Aside: Capturing static types\n\nWe define:\n\n```\ninterface Ty<T> {\n  default Type type() {\n    return requireNonNull(\n        ((ParameterizedType) getClass().getGenericInterfaces()[0])\n            .getActualTypeArguments()[0]);\n  }\n}\n```\n\nWhich lets us write:\n\n```\nType type = new Ty<Map<String, List<Integer>>>() {}.type();\n\nprintln(type);\n// Prints: Map<String, List<Integer>>\n```\n\nWhy do we need this?\n\nBecause our `witness()` method will need a runtime representation of the type\nthat it is trying to instantiate.\n\nSo our use case has turned into:\n\n```\nShow.show(witness(new Ty<>() {}), List.of(List.of(1, 2), List.of(3, 4)));\n```\n\nNote that we do not have to specify the type argument for `Ty<>`. Thankfully,\ntype inference does that for us.\n\nType inference works in this case because the `List.of(...)` parameter has a\nwell-defined type, and the call to `<A> Show.show(Show<A>, A)` lets type\ninference flow from the second argument to the first.\n\n## Aside: Understanding `java.lang.reflect.Type`'s hierarchy\n\nThe standard `java.lang.reflect.Type` hierarchy consists of:\n\n- `Class<?>`\n- `GenericArrayType`\n- `ParameterizedType`\n- `TypeVariable<?>`\n- `WildcardType`\n\nWhere:\n\n- `Class<?>`, sometimes referred to as a 'raw type', represents either:\n  - A non-generic type like `String` or `Integer`.\n  - A generic type like `Function<T, R>` where its type parameter list `[T, R]`\n    can be retrieved via `TypeVariable<?>[] getTypeParameters()`.\n- `GenericArrayType` represents an array type `E[]` whose component type `E` is\n  a `ParameterizedType` or a `TypeVariable<?>`.\n- `ParameterizedType` represents a type `T<Arg1, Arg2, ..., ArgN>`. Its\n  `getRawType()` method returns the class or interface `T`.\n- `TypeVariable<?>` represents an unbound type parameter of a generic\n  declaration like a generic class/interface or generic method.\n- `WildcardType` represents an occurrence of `?` within a `ParameterizedType`.\n\nMoreover, a `java.lang.reflect.Method` can be queried via\n`TypeVariable<?>[] getTypeParameters()`, `Type[] getGenericParameterTypes()`,\nand `Type getGenericReturnType()`.\n\nFor example:\n\n```\nclass Example {\n  static <A> List<A> hello(String s, A[] arr, Optional<?> opt) { ... }\n}\n```\n\nInspecting the `Example`'s `hello` method at runtime would yield something like:\n\n```\nMethod(\n  name: \"hello\",\n  typeParameters:\n    [TypeVariable(A)],\n  genericReturnType:\n    ParameterizedType(\n      rawType: List.class,\n      typeArguments: [TypeVariable(A)],\n    ),\n  genericParameterTypes:\n    [\n      String.class,\n      GenericArrayType(componentType: TypeVariable(A)),\n      ParameterizedType(\n        rawType: Optional.class,\n        typeArguments: [WildcardType()]\n      )\n    ]\n)\n```\n\nNote that the three ocurrences of `TypeVariable(A)` are unique in the sense that\nthey compare `equal()` to each other, but *not* to other type variable\ninstances, even if their names are also `A`.\n\n## Subgoal: Parsing `java.lang.reflect.Type` into an AST\n\nWe want to do this for a few reasons:\n\n- `java.lang.reflect.Type` is not convenient for programming:\n  - It is not a sealed hierarchy, so pattern-matching on it is error-prone.\n  - Both `Class<?>` and `GenericArrayType` may be array types.\n  - `Class<?>` may represent a primitive type, like `int` or `float`, which does\n    not participate in generic parameterization (yet).\n  - Type application as represented by `ParameterizedType` is variadic.\n    Single-parameter-a-time is easier to program against.\n- When we get to higher-kinded type classes (spoiler!), then we will need our\n  own type representation anyway.\n\nHere's the AST:\n\n```\nsealed interface ParsedType {\n  record Var(TypeVariable<?> java) implements ParsedType {}\n\n  record App(ParsedType fun, ParsedType arg) implements ParsedType {}\n\n  record ArrayOf(ParsedType elementType) implements ParsedType {}\n\n  record Const(Class<?> java) implements ParsedType {}\n\n  record Primitive(Class<?> java) implements ParsedType {}\n}\n```\n\nAnd these are our parsing rules:\n\n```\nsealed interface ParsedType {\n  // ...\n\n  static ParsedType parse(Type java) {\n    return switch (java) {\n      case Class<?> arr when arr.isArray() ->\n          new ArrayOf(parse(arr.getComponentType()));\n      case Class<?> prim when prim.isPrimitive() ->\n          new Primitive(prim);\n      case Class<?> c ->\n          new Const(c);\n      case TypeVariable<?> v ->\n          new Var(v);\n      case ParameterizedType p ->\n          parseAll(p.getActualTypeArguments()).stream()\n              .reduce(parse(p.getRawType()), App::new);\n      case GenericArrayType a ->\n          new ArrayOf(parse(a.getGenericComponentType()));\n      case WildcardType w ->\n          throw new IllegalArgumentException(\"Cannot parse wildcard type: \" + w);\n      default ->\n          throw new IllegalArgumentException(\"Unsupported type: \" + java);\n    };\n  }\n}\n```\n\n> Note: the rule for `ParameterizedType` will take a type like `T<A, B>` and\n> turn it into `App(App(Const(T), A), B)`.\n>\n> This means that the generic type `T` is first applied to `A` and then to `B`.\n\nFor example, `Map<Integer, List<String>>` becomes:\n\n```\nApp(\n  App(Const(Map.class), Const(Integer.class)),\n  App(Const(List.class), Const(String.class))\n)\n```\n\nThat's it! Really, it's not much, but the added uniformity will help our code\ndown the line.\n\n## Recap\n\nUntil this point we have:\n\n- `new Ty<T>() {}`: a way to capture a type `T` from a static context and access\n  it at runtime.\n- `ParsedType`: a uniform representation for Java's `java.lang.reflect.Type`s.\n\nNow, let's move on to the problem of type class instance resolution.\n\n## Subgoal: Witness resolution\n\nLet's consider the following scenario:\n\n```\n@TypeClass\ninterface Show<A> {\n  String show(A value);\n\n  // Convenience shortcut\n  static <A> String show(Show<A> showA, A value) {\n    return showA.show(value);\n  }\n\n  @TypeClass.Witness\n  static Show<Integer> showInteger() { ... }\n\n  @TypeClass.Witness\n  static <A> Show<List<A>> showList(Show<A> showA) { ... }\n}\n\nrecord Pair<A, B>(A fst, B snd) {\n  @TypeClass.Witness\n  public static <A, B> Show<Pair<A, B>> show(Show<A> showA, Show<B> showB) { ... }\n}\n```\n\nWe observe that, for example, in order to summon a witness for\n`Show<List<Pair<Integer, List<Integer>>>`, then we must apply several witness\nconstructors recursively.\n\n> What is a witness constructor? It is a `public static` method annotated with\n> `@TypeClass.Witness`.\n\nBut first, we must find the witness constructors!\n\nHow do we do that? Do we need to do a runtime scan of all loaded classes?\n\nThat would be way too complicated.\n\nIn order to reduce our search scope, we can define the following convention:\n\n> When trying to resolve a witness `C<T>` for some type class `C` and a concrete\n> type `T`, then we only look for witness constructors within the definitions of\n> `C` and `T` and nowhere else.\n\nFor example, when looking for `Show<Integer>`, then we scan the methods of\n`Show` and `Integer`.\n\nGenerally, we will prefer to define witness constructors within concrete types\nand not within type classes. However, for a built-in type like `Integer`, we\nhave no choice and we must define its witness constructors within the relevant\ntype classes, as we have done with `static Show<Integer> showInteger()`.\n\nNow that we are able to find the relevant witness constructors, how do we:\n\n- know which of them we must invoke,\n- and in *which order*?\n\nFirst, we must understand type unification:\n\n## Aside: Type Unification\n\nType unification is the process of finding a substitution that makes two types\nidentical.\n\nFor example, given `Pair<[A], String>` and `Pair<Integer, String>`, then the\nsubstitution `{A -> Integer}` would make the first type identical to the second.\n\n> Here, I have placed the type `A` between square brackets to indicate that it\n> is a *type variable*. We only substitute type variables!\n\nUnification may fail when it encounters incompatible types. For example,\n`List<Integer>` and `List<String>` cannot be unified because no substitution\nexists that would make them identical.\n\nConversely, when two types are already identical, unification succeeds with an\nempty substitution `{}`.\n\nBecause types are recursive, the unification algorithm must also be recursive\n(in nature).\n\nConsider:\n\n```\nunify(\n  Map<Pair<String, [T]>, List<[U]>>,\n  Map<Pair<String, Integer>, List<Optional<Boolean>>>\n)\n\n// we are unifying two types of shape:\n// - Map<K1, V1>\n// - Map<K2, V2>\n// the type constructors match on either side (Map)\n// so we recurse to their type arguments:\n// unify(K1, K2) ∪ unify(V1, V2)\n// and so on!\n\n=   unify(Pair<String, [T]>, Pair<String, Integer>)\n  ∪ unify(List<[U]>, List<Optional<Boolean>>)\n\n=   unify(String, String) // first arguments of Pair\n  ∪ unify([T], Integer)   // second arguments of Pair\n  ∪ unify([U], <Optional<Boolean>) // arguments of List\n\n=   {}\n  ∪ {T -> Integer}\n  ∪ {U -> Optional<Boolean>}\n\n= {T -> Integer, U -> Optional<Boolean>}\n\n// Done!\n```\n\nIf any unification step fails, then the entire unification fails:\n\n```\nunify(Pair<[T], Integer>, Pair<String, String>)\n\n=   unify([T], String)\n  ∪ unify(Integer, String)\n\n=   {T -> String}\n  ∪ err\n\n= err\n```\n\nFollowing is a code listing for the concrete type unification algorithm\n`unify()` for our `ParsedType` definition, along with a `subsitute()` method\nthat applies a substitution to a type.\n\n### Listing: Type Unification algorithm for `ParsedType`\n\nHere we use the `Maybe` type, which is analogous to Java's `Optional`. It is\nused to indicate that the `unify()` function may fail with a `nothing()` (empty)\ncase. This is different from it returning `just(Map.of())` (an empty map), which\nsignals success but without any substitutions. In short, based on the examples\nabove, `err` is `nothing()` and `{}` is `just(Map.of())`.\n\nA key step here is the unification of `App`. This is the main driver for the\nalgorithm's recursion.\n\nThe following helper methods are relevant to this step:\n\n```\n// applies function `f` if both `ma` and `mb` are present\n<A, B, C> Maybe<C> apply(BiFunction<A, B, C> f, Maybe<A> ma, Maybe<B> mb);\n\n// merges two maps\n// it fails at runtime if duplicate keys /with different values/ exist\n<K, V> Map<K, V> merge(Map<K, V> m1, Map<K, V> m2);\n```\n\nHere's the full code:\n\n```\nclass Unification {\n  public static Maybe<Map<ParsedType.Var, ParsedType>> unify(ParsedType t1, ParsedType t2) {\n    return switch (Pair.of(t1, t2)) {\n      case Pair<ParsedType, ParsedType>(ParsedType.Var var1, ParsedType.Primitive p) ->\n          Maybe.nothing(); // no primitives in generics\n      case Pair<ParsedType, ParsedType>(ParsedType.Var var1, var t) ->\n          Maybe.just(Map.of(var1, t));\n      case Pair<ParsedType, ParsedType>(ParsedType.Const const1, ParsedType.Const const2)\n          when const1.equals(const2) ->\n          Maybe.just(Map.of());\n      case Pair<ParsedType, ParsedType>(\n              ParsedType.App(var fun1, var arg1),\n              ParsedType.App(var fun2, var arg2)) ->\n          Maybe.apply(Maps::merge, unify(fun1, fun2), unify(arg1, arg2));\n      case Pair<ParsedType, ParsedType>(\n              ParsedType.ArrayOf(var elem1),\n              ParsedType.ArrayOf(var elem2)) ->\n          unify(elem1, elem2);\n      case Pair<ParsedType, ParsedType>(\n              ParsedType.Primitive(var prim1),\n              ParsedType.Primitive(var prim2))\n          when prim1.equals(prim2) ->\n          Maybe.just(Map.of());\n      default ->\n          Maybe.nothing();\n    };\n  }\n\n  public static ParsedType substitute(Map<ParsedType.Var, ParsedType> map, ParsedType type) {\n    return switch (type) {\n      case ParsedType.Var var ->\n          map.getOrDefault(var, var);\n      case ParsedType.App(var fun, var arg) ->\n          new ParsedType.App(substitute(map, fun), substitute(map, arg));\n      case ParsedType.ArrayOf var ->\n          new ParsedType.ArrayOf(substitute(map, var.elementType()));\n      case ParsedType.Primitive p ->\n          p;\n      case ParsedType.Const c ->\n          c;\n    };\n  }\n\n  public static List<ParsedType> substituteAll(\n      Map<ParsedType.Var, ParsedType> map, List<ParsedType> types) {\n    return types.stream().map(t -> substitute(map, t)).toList();\n  }\n}\n```\n\n### Extra: A type representation for static methods\n\n```\nrecord FuncType(Method java, List<ParsedType> paramTypes, ParsedType returnType) {\n  public static FuncType parse(Method method) {\n    if (!Modifier.isStatic(method.getModifiers())) {\n      throw new IllegalArgumentException(\"Method must be static: \" + method);\n    }\n    return new FuncType(\n        method,\n        ParsedType.parseAll(method.getGenericParameterTypes()),\n        ParsedType.parse(method.getGenericReturnType()));\n  }\n}\n```\n\n## Subgoal: Witness resolution, part 2\n\nWhy is type unification relevant?\n\nType unification will guide the witness resolution process in two ways:\n\n- By checking if any given witness constructor is relevant to our witness goal.\n- And if it *is* relevant, then it will tell us which witness subgoals to\n  resolve next.\n\nFor example, when resolving `Show<List<Integer>>`, we check:\n\n1. Which witness constructors can we find?\n\n- We scan `List`:\n  - It contains zero witness constructors.\n- We scan `Show`, and it contains:\n  - `Show<Integer> showInteger()`\n  - `<A> Show<List<A>> showList(Show<A> showA)`\n\n2. Which witness constructors apply to our goal?\n\n- Does `Show<Integer> showInteger()` apply?\n  - We try to unify `Show<Integer>` and `Show<List<Integer>>`.\n  - Unification fails.\n  - Skip this constructor ❌\n- Does `<A> Show<List<A>> showList(Show<A> showA)` apply?\n  - We try to unify `Show<List<A>>` and `Show<List<Integer>>`.\n  - Unification succeeds with the substitution `{A -> Integer}`.\n  - We can use this constructor ✅\n  - But this constructor has an argument: `Show<A>`\n  - If we apply the substitution to the argument, we find our next goal:\n    - `substitute({A -> Integer}, Show<A>)` yields `Show<Integer>`.\n  - Add `Show<Integer>` to our goals.\n\n3. Recurse until we have no further goals.\n\nThat outlines the witness resolution algorithm.\n\n## Recap\n\nUntil this point we have:\n\n- `new Ty<T>() {}`: a way to capture a type `T` from a static context and access\n  it at runtime.\n- `ParsedType`: a uniform representation for Java's `java.lang.reflect.Type`s.\n- `Unification`: an algorithm for type unification of `ParsedType`s.\n- And a rough sketch for the overall recursive witness resolution algorithm.\n\nNow, let's put it all together!\n\n## Subgoal: Witness resolution implementation\n\nLet's start with the witness constructor lookup code:\n\n```\n@Retention(RetentionPolicy.RUNTIME)\n@interface TypeClass {\n  @Retention(RetentionPolicy.RUNTIME)\n  @interface Witness {}\n}\n\nclass TypeClasses {\n  // ...\n\n  private static List<InstanceConstructor> findRules(ParsedType target) {\n    return switch (target) {\n      case ParsedType.App(var fun, var arg) ->\n          Lists.concat(findRules(fun), findRules(arg));\n      case ParsedType.Const(var java) ->\n          rulesOf(java);\n      case ParsedType.Var(var java) ->\n          List.of();\n      case ParsedType.ArrayOf(var elem) ->\n          List.of();\n      case ParsedType.Primitive(var java) ->\n          List.of();\n    };\n  }\n\n  private static List<InstanceConstructor> rulesOf(Class<?> cls) {\n    return Arrays.stream(cls.getDeclaredMethods())\n        .filter(TypeClasses::isWitnessMethod)\n        .map(FuncType::parse)\n        .map(InstanceConstructor::new)\n        .toList();\n  }\n\n  private static boolean isWitnessMethod(Method m) {\n    return m.accessFlags().contains(PUBLIC)\n        && m.accessFlags().contains(STATIC)\n        && m.isAnnotationPresent(TypeClass.Witness.class);\n  }\n\n  private record InstanceConstructor(FuncType func) {}\n\n  // ...\n}\n```\n\nNow, let's move on to how we choose relevant witness constructors and find our\nsubsequent goals:\n\n```\nclass TypeClasses {\n  // ...\n\n  private static List<Candidate> findCandidates(ParsedType target) {\n    return findRules(target).stream()\n        .flatMap(\n            rule ->\n                rule\n                    .tryMatch(target)\n                    .map(requirements -> new Candidate(rule, requirements))\n                    .stream())\n        .toList();\n  }\n\n  private record Candidate(WitnessRule rule, List<ParsedType> requirements) {}\n\n  // Spoiler: we will have another subtype of WitnessRule later on (;\n  private sealed interface WitnessRule {\n    Maybe<List<ParsedType>> tryMatch(ParsedType target);\n\n    Object instantiate(List<Object> dependencies);\n  }\n\n  private record InstanceConstructor(FuncType func) implements WitnessRule {\n    @Override\n    public Maybe<List<ParsedType>> tryMatch(ParsedType target) {\n      return Unification.unify(func.returnType(), target)\n          .map(map -> Unification.substituteAll(map, func.paramTypes()));\n    }\n\n    @Override\n    public Object instantiate(List<Object> dependencies) {\n      try {\n        return func.java().invoke(null, dependencies.toArray());\n      } catch (Exception e) {\n        throw new RuntimeException(e);\n      }\n    }\n  }\n\n  // ...\n}\n```\n\nThen, the code that drives the recursive resolution:\n\n```\nclass TypeClasses {\n  // ...\n\n  private static Either<SummonError, Object> summon(ParsedType target) {\n    return switch (ZeroOneMore.of(findCandidates(target, context))) {\n      case ZeroOneMore.One<Candidate>(Candidate(var rule, var requirements)) ->\n          summonAll(requirements, context)\n              .map(rule::instantiate)\n              .mapLeft(error -> new SummonError.Nested(target, error));\n      case ZeroOneMore.Zero<Candidate>() ->\n          Either.left(new SummonError.NotFound(target));\n      case ZeroOneMore.More<Candidate>(var candidates) ->\n          Either.left(new SummonError.Ambiguous(target, candidates));\n    };\n  }\n\n  private static Either<SummonError, List<Object>> summonAll(List<ParsedType> targets) {\n    return Either.traverse(targets, TypeClasses::summon);\n  }\n\n  private sealed interface SummonError {\n    record NotFound(ParsedType target) implements SummonError {}\n\n    record Ambiguous(ParsedType target, List<Candidate> candidates) implements SummonError {}\n\n    record Nested(ParsedType target, SummonError cause) implements SummonError {}\n  }\n\n  // ...\n}\n```\n\nAnd, finally, the public entry point for all of it:\n\n```\nclass TypeClasses {\n  public static <T> T witness(Ty<T> ty) {\n    return switch (summon(ParsedType.parse(ty.type()))) {\n      case Either.Left<SummonError, Object>(SummonError error) ->\n          throw new WitnessResolutionException(error);\n      case Either.Right<SummonError, Object>(Object instance) -> {\n        @SuppressWarnings(\"unchecked\")\n        T typedInstance = (T) instance;\n        yield typedInstance;\n      }\n    };\n  }\n\n  public static class WitnessResolutionException extends RuntimeException {\n    private WitnessResolutionException(SummonError error) {\n      super(error.format());\n    }\n  }\n\n  // ...\n}\n```\n\nSurprinsingly, that's it!\n\nYou can find a *mostly accurate* compilation of all of the above code in\n[this Gist](https://gist.github.com/Garciat/a6ca3c9195d5b1d997badecd73282e38).\n(This was the first version of the type class system that I built.)\n\nNow, let's see it in action.\n\n## Examples: First-Order Type Classes & Instances\n\n### Type Class: Show\n\nGiven:\n\n```\n@TypeClass\ninterface Show<A> {\n  String show(A a);\n\n  static <A> String show(Show<A> showA, A a) {\n    return showA.show(a);\n  }\n\n  @TypeClass.Witness\n  static Show<Integer> integerShow() {\n    return i -> Integer.toString(i);\n  }\n\n  @TypeClass.Witness\n  static Show<String> stringShow() {\n    return s -> \"\\\"\" + s + \"\\\"\";\n  }\n\n  @TypeClass.Witness\n  static <A> Show<Optional<A>> optionalShow(Show<A> showA) {\n    return optA -> optA.map(a -> \"Some(\" + showA.show(a) + \")\").orElse(\"None\");\n  }\n\n  @TypeClass.Witness\n  static <A> Show<List<A>> listShow(Show<A> showA) { ... }\n\n  @TypeClass.Witness\n  static <K, V> Show<Map<K, V>> mapShow(Show<K> showK, Show<V> showV) { ... }\n}\n```\n\nThen:\n\n```\nMap<String, List<Optional<Integer>>> m1 =\n    Map.of(\n        \"a\", List.of(Optional.of(1), Optional.empty()),\n        \"b\", List.of(Optional.of(2), Optional.of(3)));\n\nprintln(Show.show(witness(new Ty<>() {}), m1));\n// Prints: {\"a\": [Some(1), None], \"b\": [Some(2), Some(3)]}\n```\n\nNote that this requires the recursive instantiation of 5 witness constructors.\nHere's what some debug logs show:\n\n```\nInstantiating: () -> Show[A](String)\nInstantiating: () -> Show[A](Integer)\nInstantiating: ∀ A. (Show[A](A)) -> Show[A](Optional[T](A))\nInstantiating: ∀ A. (Show[A](A)) -> Show[A](List[E](A))\nInstantiating: ∀ K V. (Show[A](K), Show[A](V)) -> Show[A](Map[K, V](K)(V))\n```\n\n### Type Class: Monoid & Num\n\nGiven:\n\n```\n@TypeClass\ninterface Monoid<A> {\n  A combine(A a1, A a2);\n\n  A identity();\n\n  static <A> A combineAll(Monoid<A> monoid, List<A> elements) {\n    A result = monoid.identity();\n    for (A element : elements) {\n      result = monoid.combine(result, element);\n    }\n    return result;\n  }\n}\n\n@TypeClass\ninterface Num<A> {\n  A add(A a1, A a2);\n\n  A mul(A a1, A a2);\n\n  A zero();\n\n  A one();\n\n  @TypeClass.Witness\n  static Num<Integer> integerNum() {\n    return new Num<>() {\n      @Override\n      public Integer add(Integer a1, Integer a2) {\n        return a1 + a2;\n      }\n\n      @Override\n      public Integer mul(Integer a1, Integer a2) {\n        return a1 * a2;\n      }\n\n      @Override\n      public Integer zero() {\n        return 0;\n      }\n\n      @Override\n      public Integer one() {\n        return 1;\n      }\n    };\n  }\n}\n\nrecord Sum<A>(A value) {\n  @TypeClass.Witness\n  public static <A> Monoid<Sum<A>> monoid(Num<A> num) {\n    return new Monoid<>() {\n      @Override\n      public Sum<A> combine(Sum<A> s1, Sum<A> s2) {\n        return new Sum<>(num.add(s1.value(), s2.value()));\n      }\n\n      @Override\n      public Sum<A> identity() {\n        return new Sum<>(num.zero());\n      }\n    };\n  }\n}\n```\n\nThen:\n\n```\nvar sums = List.of(new Sum<>(3), new Sum<>(5), new Sum<>(10));\n\nprintln(Monoid.combineAll(witness(new Ty<>() {}), sums));\n// Prints: Sum[value=18]\n```\n\n### Type Class: PrintAll\n\nThis example is based on:\n<https://wiki.haskell.org/Varargs>\n\nIt abuses type classes in order to implement variadic functions. Please read the\nlink above to understand how this works.\n\nIt's really striking how it *just works* with our system!\n\nGiven:\n\n```\n@TypeClass\ninterface PrintAll<T> {\n  T printAll(List<String> strings);\n\n  static <T> T of(PrintAll<T> printAll) {\n    return printAll.printAll(List.of());\n  }\n\n  @TypeClass.Witness\n  static PrintAll<Void> base() {\n    return strings -> {\n      for (String s : strings) {\n        System.out.println(s);\n      }\n      return null;\n    };\n  }\n\n  @TypeClass.Witness\n  static <A, R> PrintAll<Function<A, R>> func(Show<A> showA, PrintAll<R> printR) {\n    return strings -> a -> printR.printAll(Lists.concat(strings, List.of(showA.show(a))));\n  }\n}\n```\n\nThen:\n\n```\nFunction<String, Function<List<String>, Function<Integer, Void>>> printer =\n    PrintAll.of(witness(new Ty<>() {}));\n\nprinter.apply(\"Items:\").apply(JavaList.of(\"apple\", \"banana\", \"cherry\")).apply(42);\n// Prints:\n// \"Items:\"\n// [\"apple\", \"banana\", \"cherry\"]\n// 42\n```\n\n### Type Class: Type Equality!\n\nThis examples shows how we can encode propositions about types themselves and\nhave the resolver construct evidence for them.\n\nReified type equality is very neat construct that I'd like to write more about\nsoon.\n\nFor now, let's appreciate how this neatly encodes\n[Haskell's own type equality](https://hackage.haskell.org/package/base-4.21.0.0/docs/Data-Type-Equality.html)\nin Java.\n\nConsider:\n\n```\n@TypeClass\nsealed interface TyEq<A, B> {\n  A castR(B b);\n\n  B castL(A a);\n\n  @TypeClass.Witness\n  static <T> TyEq<T, T> refl() {\n    return new Refl<>();\n  }\n\n  record Refl<T>() implements TyEq<T, T> {\n\n    @Override\n    public T castR(T t) {\n      return t;\n    }\n\n    @Override\n    public T castL(T t) {\n      return t;\n    }\n  }\n}\n```\n\n`TyEq<A, B>` represents the proposition that `A = B`.\n\nAnd the only possible constructor for it is `refl()`, which can only prove that\n`TyEq<T, T>` for any `T`.\n\nHow is that useful?\n\n`TyEq<T, T>` may be trivial in the static context where it is constructed. But\nit certainly is not in the context where it may be needed.\n\nTake a look:\n\n```\nclass List<T> {\n  // ...\n\n  int sum(TyEq<T, Integer> ty) {\n    int s = 0;\n    for (T item : this) {\n      s += ty.castL(item); // casts T to Integer !\n    }\n    return s;\n  }\n\n  // ...\n}\n\n// then\n\nvar xs = List.of(1, 2, 3);\n\nxs.sum(refl());\n// Returns: 6\n```\n\nWithin `List<T>`, we have no idea what `T`. Code can make no assumptions about\nit. However, if we have a proof that `T = Integer`, then we *can* sum the\nelements up into an `int`.\n\nNow, we can also request them as witnesses in a witness constructor:\n\n```\n@TypeClass\ninterface SumAllInt<A> {\n  A sum(List<Integer> list);\n\n  static <T> T of(SumAllInt<T> sumAllInt) {\n    return sumAllInt.sum(List.of());\n  }\n\n  @TypeClass.Witness\n  static SumAllInt<Integer> base() {\n    return list -> list.stream().mapToInt(Integer::intValue).sum();\n  }\n\n  @TypeClass.Witness\n  static <A, R> SumAllInt<Function<A, R>> func(TyEq<A, Integer> eq, SumAllInt<R> sumR) {\n    return list -> a -> sumR.sum(Lists.concat(list, List.of(eq.castL(a))));\n  }\n}\n```\n\nSimilar to the `PrintAll` example, this lets us summon variadic functions:\n\n```\nFunction<Integer, Function<Integer, Function<Integer, Integer>>> sum =\n    SumAllInt.of(witness(new Ty<>() {}));\nprintln(sum.apply(1).apply(2).apply(3));\n```\n\nHowever, notice how in the `func` rule, we requested `TyEq<A, Integer>`. This\nconstrains the `A` type argument to just `Integer`.\n\nFunny enough, this is actually only necessary in Haskell due to how integer\nliterals are overloaded. So I implemented this here for no gain. But it was cool\nthat it just worked!\n\n## Goal: Higher-Order Type Classes\n\nConsider the `Functor` type class in Haskell:\n\n```\nclass Functor f where\n  fmap :: (a -> b) -> f a - > f b\n```\n\nNotice how `f` is not a type?\n\nIt is not one because it is being applied to types `a` and `b`, respectively.\n\nThis means that `f` is a *type constructor*.\n\n> A type constructor is a sort of type-level *function* that builds new types\n> from existing types.\n>\n> Here, we mean *function* in the sense that it has the mathematical property of\n> being *injective*. That is, no two inputs map to the same output.\n>\n> In practice, this means that if we know that the output is the same, then the\n> input must be the same:\n>\n> `F<X> = F<Y> ⇒ X = Y`\n>\n> This property is what allows us to unify under type constructors.\n\nIn Java, `class List<T>` can be seen as a type constructor. Though we don't use\ntype application syntax as in Haskell, we do use type parameterization syntax:\n`List<Integer>`. In Java, generic types cannot be partially applied. That is,\nfor a type `class C<T1, T2, ..., TN>`, we must provide all `N` type arguments at\nonce.\n\nLet's try representing the `Functor` type class with a Java interface:\n\n```\ninterface Functor<F> {\n  <A, B> F<B> fmap(Function<A, B> f, F<A> fa);\n}\n```\n\nUh-oh:\n\n```\nThe type F is not generic; it cannot be parameterized with arguments <B>\n```\n\nIndeed, Java requires that the type parameter `F` is a *type*, and **not a type\nconstructor**.\n\nAre we cooked?\n\nNot quite.\n\n## Aside: What is a kind?\n\nSimply: values have types; types have kinds.\n\n`42` has type `Integer`.\n\n`Integer` has kind `*` (read: star).\n\n> The syntax `*` for the kind of plain types comes from Haskell.\n>\n> We could also say `Integer` has kind `Type`, but that may be confusing given\n> how overloaded the word 'type' is in this context.\n\n`List<Integer>` also has kind `*`.\n\nSo what is the kind of `List` itself?\n\n`List` has kind `* -> *`.\n\nThat is, it is a type-level function that accepts a type and returns a type.\n\n## Aside: Higher-Kinded Types in Java\n\nHere we introduce an *encoding* of higher-kinded types for Java.\n\nRecall that interface `Functor<F>` failed because Java insists `F` is a concrete\ntype, not a type constructor. This encoding gives us a way to pretend `F` is\nhigher-kinded.\n\n> Note: I did not invent this encoding. I have seen several projects using it\n> (see the Related Work section). I am not sure where it originated, but it is\n> rather elegant.\n\nConsider:\n\n```\ninterface TApp<F, A> {}\n\nabstract class TagBase {}\n\nsealed interface Maybe<A> extends TApp<Maybe.Tag, A> {\n  record Nothing<A>() implements Maybe<A> {}\n  \n  record Just<A>(A value) implements Maybe<A> {}\n\n  final class Tag extends TagBase {}\n\n  static <A> Maybe<A> unwrap(TApp<Maybe.Tag, A> m) {\n    return (Maybe<A>) m; // unsafe if we misuse TApp and tag types\n  }\n}\n```\n\nLet's unpack this:\n\n- `TApp<F, A>` represents type application, hence its name.\n  - We assume that `F` has kind `* -> *` and `A` has kind `*`.\n  - `TApp<F, A>` represents `F<A>`.\n  - Therefore `TApp<F, A>`, in principle, has kind `*`.\n- `Maybe.Tag` is a sort of proxy type for `Maybe` as an unapplied type\n  constructor.\n  - `TApp<Maybe.Tag, A>`, in principle, means `Maybe<A>`.\n  - That is why `Maybe<A> extends TApp<Maybe.Tag, A>`.\n- `Maybe.Tag` extends `TagBase` just so that we can easily identify this type\n  via reflection later.\n\nHow is this useful?\n\nNow we *do* have a mechanism (more of a convention) to represent `Functor`!\n\nCheck it out:\n\n```\ninterface Functor<F> {\n  <A, B> TApp<F, B> fmap(Function<A, B> f, TApp<F, A> fa);\n}\n```\n\nRemember: `TApp<F, A>` means `F<A>`.\n\nAnd we can also define the witness constructor:\n\n```\nsealed interface Maybe<A> extends TApp<Maybe.Tag, A> {\n  // ...\n\n  default <B> Maybe<B> map(Function<A, B> f) { ... }\n\n  @TypeClass.Witness\n  static Functor<Maybe.Tag> functor() {\n    return new Functor<>() {\n      @Override\n      public <A, B> TApp<Maybe.Tag, B> fmap(Function<A, B> f, TApp<Maybe.Tag, A> fa) {\n        return unwrap(fa).map(f);\n      }\n    };\n  }\n}\n```\n\nLet's unpack that:\n\n- We define a witness constructor for `Functor<Maybe.Tag>`.\n  - Remember: `Functor<Maybe.Tag>` means `Functor<Maybe>`, where `Maybe` is the\n    unapplied type constructor.\n- We then have to implement:\n  - `<A, B> TApp<Maybe.Tag, B> fmap(Function<A, B> f, TApp<Maybe.Tag, A> fa)`\n  - And remember:\n    - `TApp<Maybe.Tag, A>` means `Maybe<A>`\n    - `TApp<Maybe.Tag, B>` means `Maybe<B>`\n  - If we squint our eyes a bit, *it does make sense*.\n- We use `unwrap()` to convert from `TApp<Maybe.Tag, A>` to `Maybe<A>`.\n- `Maybe<B>` is a subtype of `TApp<Maybe.Tag, B>`, so the return type just\n  works.\n\nThat's it!\n\nNow, we must extend our type class system to understand these typing\nconventions.\n\n## Subgoal: Extending `ParsedType` to support HKTs\n\nThis is actually not very involved. Check it out:\n\n```\nsealed interface ParsedType {\n  // ...\n\n  static ParsedType parse(Type java) {\n    return switch (java) {\n      // New:\n      case Class<?> tag\n        when parseTagType(tag) instanceof Maybe.Just<Class<?>>(var tagged) ->\n          new Const(tagged);\n      // New:\n      case ParameterizedType p\n          when parseAppType(p)\n              instanceof Maybe.Just<Pair<Type, Type>>(Pair<Type, Type>(var fun, var arg)) ->\n          new App(parse(fun), parse(arg));\n      // etc\n    };\n  }\n\n  private static Maybe<Class<?>> parseTagType(Class<?> c) {\n    return switch (c.getEnclosingClass()) {\n      case Class<?> enclosing when c.getSuperclass().equals(TagBase.class) ->\n          Maybe.just(enclosing);\n      case null -> Maybe.nothing();\n      default -> Maybe.nothing();\n    };\n  }\n\n  private static Maybe<Pair<Type, Type>> parseAppType(ParameterizedType t) {\n    return switch (t.getRawType()) {\n      case Class<?> raw when raw.equals(TApp.class) ->\n          Maybe.just(Pair.of(t.getActualTypeArguments()[0], t.getActualTypeArguments()[1]));\n      default -> Maybe.nothing();\n    };\n  }\n}\n```\n\nWe only had to add two new pattern-match cases to `parse`:\n\n- `case Class<?> tag when parseTagType(tag) ... tagged`\n  - `-> new Const(tagged)`\n  - This replaces any occurrence of `T.Tag` with `T` itself.\n- `case ParameterizedType p when parseAppType(p) ... (fun, arg)`\n  - `-> new App(parse(fun), parse(arg))`\n  - This replaces any occurrence of `TApp<F, A>` with\n    `new App(parse(F), parse(A))`.\n\nThat's it!\n\nI was also surprised! The witness resolution code needs no changes whatsoever!\n\nAlthough, I was worried about potential bugs coming from misuses of `Tag`s and\n`TApp`. So I came up with a lightweight embedded kind-checking mechanism.\n\n## Aside: Kind-checking Java-embedded HKTs\n\nGiven:\n\n```\ninterface Kind<K extends Kind.Base> {\n  sealed interface Base {}\n\n  // KStar = *\n  final class KStar implements Base {}\n\n  // KArr k = * -> k\n  final class KArr<K extends Base> implements Base {}\n}\n\nabstract class TagBase<K extends Kind.Base> implements Kind<K> {}\n\n// Full application of a unary type constructor\n// TApp :: (* -> *) -> * -> *\ninterface TApp<Tag extends Kind<KArr<KStar>>, A> extends Kind<KStar> {}\n\n// Partial application of a binary type constructor\n// TPar :: (* -> * -> *) -> * -> (* -> *)\ninterface TPar<Tag extends Kind<KArr<KArr<KStar>>>, A> extends Kind<KArr<KStar>> {}\n```\n\n- `TApp` now can only apply to tags of kind `* -> *`, itself becoming a kind\n  `*`.\n- `TPar` applies to tags of kind `* -> * -> *`, itself becoming a kind `* -> *`.\n- This gives us some rudimentary kind-checking in Java\n\nThen:\n\n```\nsealed interface Maybe<A> extends TApp<Maybe.Tag, A> {\n  // ...\n\n  final class Tag extends TagBase<KArr<KStar>> {}\n\n  static <A> Maybe<A> unwrap(TApp<Tag, A> value) {\n    return (Maybe<A>) value;\n  }\n}\n```\n\nAnd also:\n\n```\n@FunctionalInterface\ninterface State<S, A> extends TApp<TPar<State.Tag, S>, A> {\n  // ...\n\n  @TypeClass.Witness\n  static <S> Functor<TPar<Tag, S>> functor() { ... }\n\n  final class Tag extends TagBase<KArr<KArr<KStar>>> {}\n\n  static <S, A> State<S, A> unwrap(TApp<TPar<Tag, S>, A> value) {\n    return (State<S, A>) value;\n  }\n}\n```\n\nNotice in this case that `State` has two type parameters:\n\n- `State.Tag` has kind `KArr<KArr<KStar>>`\n  - This means: `* -> * -> *`\n- The first application of `State.Tag` must go through `TPar`.\n- The subsequent application must go through `TApp`.\n\n## Examples: Higher-Kinded Type Clases\n\n### Type Class: Traversable\n\nAccording to Haskell's\n[documentation on Traversable](https://hackage.haskell.org/package/base-4.21.0.0/docs/Data-Traversable.html):\n\n> Traversable is the class of data structures that can be traversed from left to\n> right, performing an action on each element.\n\nThe Haskell type class looks like this:\n\n```\nclass (Functor t, Foldable t) => Traversable t where\n  traverse :: Applicative f => (a -> f b) -> t a -> f (t b)\n```\n\nNotice the use of the following features:\n\n- `Traversable` is higher-kinded.\n  - Its parameter `t` has kind `* -> *`.\n- `Traversable` has two superclasses: `Functor` and `Foldable`.\n- Its method `traverse` is quantified on three type variables:\n  - `a` and `b` are plain types, of kind `*`.\n  - `f` is of kind `* -> *`, as it seen applied as `f b` and `f (t b)`.\n- The type variable `f` has the constraint `Applicative f`.\n\n`Traversable` is a rather advanced type class.\n\nLet's translate it into Java.\n\nFor now, let's ignore its superclasses:\n\n```\n@TypeClass\ninterface Traversable<T extends Kind<KArr<KStar>>> {\n\n  <F extends Kind<KArr<KStar>>, A, B>\n      TApp<F, ? extends TApp<T, B>> traverse(\n          Applicative<F> applicative,\n          Function<A, ? extends TApp<F, B>> f,\n          TApp<T, A> t);\n\n  // And a static helper, for convenience:\n  // (I omitted some types and arguments for brevity)\n  static <F, A, B> ... traverse(\n      Traversable<T> traversable,\n      Applicative<F> applicative,\n      ...) {\n    return traversable.traverse(applicative, f, t);\n  }\n}\n```\n\nWe see that:\n\n- `T` has kind `KArr<KStar>` which means `* -> *`.\n- The method `traverse`:\n  - Is generic on three type variables: `F`, `A`, `B`.\n    - `F` is also declared as having kind `* -> *`.\n  - Depends on a witness of `Applicative<F>`.\n  - Declares covariance `TApp<T, B>` on a couple of places:\n    - Because we know that we won't return values of static type `TApp<T, B>`\n      itself, but rather values of type `T<B>`, which in our HKT convention will\n      extend `TApp<T, B>`.\n\nThen:\n\n```\n// Unfortunately for HKTs, we must wrap regular Java lists\nrecord JavaList<A>(List<A> toList) implements TApp<JavaList.Tag, A> {\n  // ...\n\n  @TypeClass.Witness\n  public static <A> Show<JavaList<A>> show(Show<A> showA) { ... }\n\n  @TypeClass.Witness\n  public static Functor<JavaList.Tag> functor() { ... }\n\n  @TypeClass.Witness\n  public static Traversable<JavaList.Tag> traversable() { ... }\n\n  // ...\n}\n\n// and:\n\nsealed interface Maybe<A> {\n  // ...\n\n  @TypeClass.Witness\n  static Applicative<Maybe.Tag> applicative() { ... }\n\n  // ...\n}\n```\n\nFor example:\n\n```\nTraversable.traverse(\n    witness(new Ty<>() {}), // for Traversable<JavaList>\n    witness(new Ty<>() {}), // for Applicative<Maybe>\n    JavaList.of(1, 2, 3),\n    Maybe::just);\n// Returns: Just[value=JavaList[toList=[1, 2, 3]]]\n```\n\n### Type Class: Functor, Applicative, Monad\n\nAs expected:\n\n```\n@TypeClass\ninterface Functor<F extends Kind<KArr<KStar>>> {\n  <A, B> TApp<F, B> map(Function<A, B> f, TApp<F, A> fa);\n}\n\n@TypeClass\ninterface Applicative<F extends Kind<KArr<KStar>>> extends Functor<F> {\n  <A> TApp<F, A> pure(A a);\n\n  <A, B> TApp<F, B> ap(TApp<F, Function<A, B>> ff, TApp<F, A> fa);\n\n  @Override\n  default <A, B> TApp<F, B> map(Function<A, B> f, TApp<F, A> fa) {\n    return ap(pure(f), fa);\n  }\n}\n\n@TypeClass\ninterface Monad<M extends Kind<KArr<KStar>>> extends Applicative<M> {\n  <A, B> TApp<M, B> flatMap(Function<A, TApp<M, B>> f, TApp<M, A> fa);\n\n  @Override\n  default <A, B> TApp<M, B> map(Function<A, B> f, TApp<M, A> fa) {\n    return flatMap(a -> pure(f.apply(a)), fa);\n  }\n\n  @Override\n  default <A, B> TApp<M, B> ap(TApp<M, Function<A, B>> ff, TApp<M, A> fa) {\n    return flatMap(a -> map(f -> f.apply(a), ff), fa);\n  }\n}\n```\n\nI currently don't have any interesting `Monad` examples. I may add one or two\nsoon.\n\n## Recap\n\nWe have built:\n\n- A simple mechanism to capture static types for runtime access.\n- An AST and a parser for Java types.\n  - Including support for higher-kinded types encoded via 'tag types'.\n- Type unification on said AST of types.\n- A witness resolution algorithm that:\n  - For a witness type `C<T>`, finds witness constructors in `C` and `T`.\n    - A witness constructor is a public static method annotated with\n      `@TypeClass.Witness`.\n  - Uses type unification to:\n    - Filter relevant witness constructors.\n    - Guide the recursive search for witness dependencies.\n\nAnd altogether:\n\n- We are able to resolve arbitrarily nested witnesses for first-order type\n  clases like `Eq`, `Ord`, and `Monoid` and also for higher-kinded type classes\n  like `Functor`, `Monad`, and `Traversable`.\n- The witness summoning syntax looks like:\n  - `C<T> w = witness(new Ty<>() {});`\n- Witness resolution is currently done at runtime. Ideally, we should be able to\n  do this work at compile time, as that's where the power of type classes lies\n  in languages like Haskell.\n\n## Conclusion\n\nThis was **a lot** of fun to work on. I had never before implemented type\nclasses in a language, and it turned out to be way simpler than I thought. The\nkey insight was figuring out that type unification was all that I needed.\nStupidly, I did not check Haskell's spec to figure that out. But it did come\nsomewhat naturally after a bit of thinking, given my experience in implementing\ntype systems.\n\nThis project was partially inspired by\n[this recent Brian Goetz JVMLS talk](https://www.youtube.com/watch?v=Gz7Or9C0TpM)\nwhere he presents his early ideas on how to bring type clases to Java. His\nproposed syntax is `Show<Integer>.witness`. And, of course, the mechanism is\nsupposed to resolve witnesses at compile time.\n\nYou can find the complete implementation in\n[this Gist](https://gist.github.com/Garciat/204226a528018fa7d10abb93fa51c4ca).\n\nThe code also contains several other type class examples like QuickCheck's\n[Arbitrary type class](https://hackage-content.haskell.org/package/QuickCheck-2.17.1.0/docs/Test-QuickCheck-Arbitrary.html).\n\n### Future work\n\n- The resolution mechanism could use some caching:\n  - For witness constructor lookups.\n  - For witness instances themselves.\n- It would be ideal to shift witness resolution to compile time.\n  - Perhaps with a javac plugin or something like that?\n\n### Related Work\n\nHere are some related projects and posts:\n\n- [HighJ](https://github.com/DanielGronau/highj)\n\n  - HKT encoding via `Higher<F, A>`.\n  - Provides Functor/Monad/etc.\n  - **No automatic instance resolution**, instances are explicit classes.\n- [Cyclops React](https://github.com/aol/cyclops)\n\n  - Functional programming toolkit for Java.\n  - Type class-like interfaces (Functor, Monad, etc.).\n  - **Manual instance lookup**, no reflection or unification.\n- [Higher-Kinded-J](https://github.com/higherkindness/higherkinded-j)\n\n  - Modern HKT encoding using witness types.\n  - Supports Functor, Applicative, Monad, MonadError, etc.\n  - **Instances must be explicitly provided**, not inferred.\n- [mduerig/java-functional](https://github.com/mduerig/java-functional)\n\n  - Experimental HKT + type class encodings in Java.\n  - Conceptually related exploration.\n  - **No generic instance search or unification engine**.\n- [Type Classes in Java (blog post)](https://zarinfam.github.io/2018/12/01/type-classes-in-java/)\n\n  - Demonstrates type class pattern using interfaces.\n  - **Requires manual wiring of instances**.\n- [JavaGI](https://www.infosun.fim.uni-passau.de/cl/staff/sulzmann/javagi/)\n\n  - Research extension to Java with Haskell-style type classes.\n  - Uses **unification-based instance resolution**.\n  - **Requires its own compiler**, not a Java library.\n\n## Annex: Context Instances\n\nConsider this example:\n\n```\nstatic <A> String example(Show<A> showA, A value) {\n  return Show.show(witness(new Ty<>() {}), JavaList.of(value));\n}\n```\n\nIt is equivalent to the following Haskell code:\n\n```\nexample :: Show a => a -> String\nexample value = show [value]\n```\n\nIn the Java code, we try to lookup `Show<List<A>>`, but we don't know what `A`\nis!\n\nSure, at runtime we may know its real type, but we actually would like\nresolution to be static. (Even though we use reflection for witness resolution.)\n\nIn the Haskell code, the available instance of `Show a` as captured by the\nfunction's signature becomes available as a 'context instance' that is used to\nderive `Show [a]` for the call of `show`.\n\nHow can we achieve this in Java?\n\nFirst, we define a type that can capture a witness along with its static type:\n\n```\nabstract class Ctx<T> {\n  private final T instance;\n\n  Ctx(T instance) {\n    this.instance = instance;\n  }\n\n  public T instance() {\n    return instance;\n  }\n\n  public Type type() {\n    return requireNonNull(\n        ((ParameterizedType) getClass().getGenericSuperclass())\n            .getActualTypeArguments()[0]);\n  }\n}\n```\n\nIt leverages the same mechanism as `Ty<T>` to capture static types.\n\nThen, we pass it to the `witness()` method:\n\n```\nstatic <A> String example(Show<A> showA, A value) {\n  return Show.show(\n      witness(new Ty<>() {}, new Ctx<>(showA) {}),\n      JavaList.of(value));\n}\n```\n\nFinally, we update a bit of our type class resolution code:\n\n```\nclass TypeClasses {\n  // New: second parameter\n  public static <T> T witness(Ty<T> ty, Ctx<?>... context) {\n    return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) {\n      // ...\n    };\n  }\n\n  // New: parsing Ctx<?> into ContextInstance\n  private static List<ContextInstance> parseContext(Ctx<?>[] context) {\n    return Arrays.stream(context)\n        .map(ctx -> new ContextInstance(ctx.instance(), ParsedType.parse(ctx.type())))\n        .toList();\n  }\n\n  // ...\n\n  // New: second parameter\n  private static Either<SummonError, Object> summon(\n      ParsedType target, List<ContextInstance> context) {\n    // ...\n  }\n\n  // New: second parameter\n  private static Either<SummonError, List<Object>> summonAll(\n      List<ParsedType> targets, List<ContextInstance> context) {\n    // ...\n  }\n\n  // New: second parameter\n  private static List<Candidate> findCandidates(\n      ParsedType target, List<ContextInstance> context) {\n    // New: use the context instances along with the discovered witness constructors!\n    return Stream.<WitnessRule>concat(\n            context.stream(),\n            findRules(target).stream())\n        .flatMap(...)\n        .toList();\n  }\n\n  // ...\n\n  // New: a new case class for WitnessRule\n  private record ContextInstance(Object instance, ParsedType type) implements WitnessRule {\n    @Override\n    public Maybe<List<ParsedType>> tryMatch(ParsedType target) {\n      // This is a concrete instance, so we only check for type equality\n      return target.equals(type) ? Maybe.just(List.of()) : Maybe.nothing();\n    }\n\n    @Override\n    public Object instantiate(List<Object> dependencies) {\n      // Trivial\n      return instance;\n    }\n  }\n}\n```\n\nThat's all. A bit noisy, but rather simple.\n\nNow this works:\n\n```\nstatic <A> String example(Show<A> showA, A value) {\n  return Show.show(\n      witness(new Ty<>() {}, new Ctx<>(showA) {}),\n      JavaList.of(value));\n}\n\nprintln(example(witness(new Ty<>() {}), 123));\n```\n\nHere, the type captured by `new Ctx<>(showA) {}` is `Show<A>`, where `A` is the\n*unique type variable* belonging to the `example` method.\n\nThe `Show<List<A>>` witness that we are trying to summon needs a `Show<A>` whose\ntype could only possibly exist in this static context!\n\n## Annex: Overlapping Instances\n\nIn Haskell, the `String` type is defined as:\n\n```\ntype String = [Char]\n```\n\nThat is, a `String` is just a list of `Char`.\n\nNow, notice the difference here:\n\n```\nshow [1, 2, 3]\n-- \"[1, 2, 3]\"\n\nshow [('a', 1), ('b', 2)]\n-- \"[('a', 1), ('b', 2)]\"\n\nshow ['a', 'b']\n-- \"\\\"ab\\\"\" what?\n```\n\nThe generic `Show` instance for `[a]` simply intercalates `\", \"` between\nelements.\n\nBut the `Show` instance for `[Char]` behaves differently.\n\nWhy is that? This is due to a language extension called\n[overlapping instances](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/instances.html#overlapping-instances).\n\nIt allows otherwise ambiguous instances to coexist:\n\n```\ninstance Show a => Show [a] where ...\n\ninstance {-# OVERLAPPING #-} Show [Char] where ...\n```\n\nThe `OVERLAPPING` pragma tells the compiler that this instance may override\nanother instance iff it is more specific.\n\nThe rules for instance specificity are explained in the same link.\n\nNow, consider in Java:\n\n```\nsealed interface FwdList<A> extends TApp<FwdList.Tag, A> {\n  record Nil<A>() implements FwdList<A> {}\n\n  record Cons<A>(A head, FwdList<A> tail) implements FwdList<A> {}\n\n  @TypeClass.Witness\n  static <A> Show<FwdList<A>> show(Show<A> showA) { ... }\n\n  // Ambiguous!\n  @TypeClass.Witness\n  static Show<FwdList<Character>> show() { ... }\n}\n```\n\n`FwdList` (name inspired by\n[C++'s std::forward\\_list](https://en.cppreference.com/w/cpp/container/forward_list.html))\nimplements a data structure like Haskell's lists.\n\nAs it is, witness resolution will fail with an ambiguous witness error.\n\nIn order to support overlapping instance, we must apply some changes.\n\nFirst, let's model Haskell's `OVERLAPPING` and `OVERLAPPABLE` pragmas:\n\n```\n@Retention(RetentionPolicy.RUNTIME)\n@interface TypeClass {\n  @Retention(RetentionPolicy.RUNTIME)\n  @interface Witness {\n    Overlap overlap() default Overlap.NONE;\n\n    enum Overlap {\n      NONE,\n      OVERLAPPING,\n      OVERLAPPABLE\n    }\n  }\n}\n```\n\nThen, we make it accessible from `InstanceConstructor`:\n\n```\nprivate record InstanceConstructor(FuncType func) implements WitnessRule {\n  public TypeClass.Witness.Overlap overlap() {\n    return func.java().getAnnotation(TypeClass.Witness.class).overlap();\n  }\n\n  // ...\n}\n```\n\nFinally, we implement the overlapping instances deduction algorithm as described\nin\n[Haskell's spec](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/instances.html#overlapping-instances):\n\n```\nprivate static List<InstanceConstructor> reduceOverlapping(\n    List<InstanceConstructor> candidates) {\n  return candidates.stream()\n      .filter(\n          iX ->\n              candidates.stream()\n                  .filter(iY -> iX != iY)\n                  .noneMatch(iY -> isOverlappedBy(iX, iY)))\n      .toList();\n}\n\nprivate static boolean isOverlappedBy(\n    InstanceConstructor iX, InstanceConstructor iY) {\n  return (iX.overlap() == OVERLAPPABLE || iY.overlap() == OVERLAPPING)\n      && isSubstitutionInstance(iX, iY)\n      && !isSubstitutionInstance(iY, iX);\n}\n\nprivate static boolean isSubstitutionInstance(\n    InstanceConstructor base, InstanceConstructor reference) {\n  return Unification.unify(base.func().returnType(), reference.func().returnType())\n      .fold(() -> false, map -> !map.isEmpty());\n}\n```\n\nAnd we apply it to our candidate resolution function:\n\n```\nprivate static List<Candidate> findCandidates(\n    ParsedType target, List<ContextInstance> context) {\n  return Stream.<WitnessRule>concat(\n          context.stream(),\n          reduceOverlapping(findRules(target)).stream())\n      .flatMap(...)\n      .toList();\n}\n```\n\nAnd, of course, we annotate our witness constructor:\n\n```\nsealed interface FwdList<A> extends TApp<FwdList.Tag, A> {\n  record Nil<A>() implements FwdList<A> {}\n\n  record Cons<A>(A head, FwdList<A> tail) implements FwdList<A> {}\n\n  @TypeClass.Witness\n  static <A> Show<FwdList<A>> show(Show<A> showA) { ... }\n\n  // OK now!\n  @TypeClass.Witness(overlap = OVERLAPPING)\n  static Show<FwdList<Character>> show() { ... }\n}\n```\n\nNow, our witness resolution code will pick `Show<FwdList<Character>>` because it\nis more specific AND it declares that it may overlap other instances.\n\n---\n\n*Originally published at [garciat.com](https://garciat.com/posts/java-type-classes/).*\n","body_html":"<h1 id=\"full-haskell-like-type-class-resolution-in-java\">Full Haskell-like Type Class resolution in Java</h1>\n<p><em>Gabriel Garcia · December 6, 2025</em></p>\n<h2 id=\"tl-dr\">TL;DR</h2>\n<p>This post explores how far Java’s type system can be pushed by building a small\nHaskell-style type class instance resolution engine entirely in plain Java.</p>\n<p>Using only reflection, generic type metadata, and a tiny first-order unifier, we\ncan automatically resolve type class instances (including higher-kinded ones),\nsupport overlapping instances, and derive witnesses for arbitrarily nested\ngeneric types.</p>\n<p>It’s not intended for production, but as an experiment it reveals just how much\nstructure Java actually preserves at runtime — and how surprisingly close it can\nget to type-level programming without language changes.</p>\n<p>You can also jump straight to the full implemention\n<a href=\"https://gist.github.com/Garciat/204226a528018fa7d10abb93fa51c4ca\" rel=\"nofollow ugc noopener\">in this gist</a>.</p>\n<p>Update: I published a GitHub repo for the project at\n<a href=\"https://github.com/garciat/java-type-classes\" rel=\"nofollow ugc noopener\">github.com/garciat/java-type-classes</a>.</p>\n<h2 id=\"intro-what-are-type-classes\">Intro: What are Type Classes?</h2>\n<p>Consider:</p>\n<pre><code>interface Equatable&lt;T&gt; {\n  boolean eq(T other);\n}\n\nrecord Point(int x, int y) implements Equatable&lt;Point&gt; {\n  @Override\n  public boolean eq(Point other) {\n    return x == other.x() &amp;&amp; y == other.y();\n  }\n}</code></pre>\n<p>That&#39;s a pretty simple OOP-style generic interface.</p>\n<p>However:</p>\n<pre><code>record Dup&lt;A&gt;(A fst, A snd) implements Equatable&lt;Dup&lt;A&gt;&gt; {\n  @Override\n  public boolean eq(Dup&lt;A&gt; other) {\n    return fst.eq(other.fst()) &amp;&amp; snd.eq(other.snd());\n    // Error: no method eq() in type A!\n  }\n}</code></pre>\n<p>We can&#39;t implement <code>eq()</code> for <code>Dup</code> without knowing the fact that\n<code>A extends Equatable&lt;A&gt;</code>.</p>\n<p>So let&#39;s try that:</p>\n<pre><code>record Dup&lt;A extends Equatable&lt;A&gt;&gt;(A fst, A snd) implements Equatable&lt;Dup&lt;A&gt;&gt; {\n  @Override\n  public boolean eq(Dup&lt;A&gt; other) {\n    return fst.eq(other.fst()) &amp;&amp; snd.eq(other.snd());\n    // OK\n  }\n}</code></pre>\n<p>Now the code compiles.</p>\n<p>However, <code>Dup</code> is now only compatible with types that extend <code>Equatable</code>. <code>Dup</code>\nis now less capable.</p>\n<p>We could have checked at runtime if each object is <code>instanceof Equatable</code>, but\nthat doesn&#39;t work very well with generics. And it may fail at runtime.</p>\n<p>Ideally, there would be a way to say:</p>\n<p><code>Dup</code> implements <code>Equatable&lt;Dup&lt;A&gt;&gt;</code> <strong>if and only if</strong> <code>A</code> implements\n<code>Equatable&lt;A&gt;</code>.</p>\n<blockquote><p>Also, if you think about it, it is a bit strange that the definition of\nequality for a type <code>A</code> depends on the object instance. As in, <code>eq()</code> is an\ninstance method.</p>\n<p>It would make more sense if equality depended only on the type <code>A</code> itself.\nRight?</p></blockquote>\n<p>That&#39;s where type clases come in:</p>\n<pre><code>interface Eq&lt;A&gt; {\n  boolean eq(A x, A y);\n}\n\nrecord Point(int x, int y) {\n  public static Eq&lt;Point&gt; eq() {\n    return (p1, p2) -&gt;\n        p1.x() == p2.x()\n        &amp;&amp; p1.y() == p2.y();\n  }\n}\n\nrecord Dup&lt;A&gt;(A fst, A snd) {\n  public static &lt;A&gt; Eq&lt;Dup&lt;A&gt;&gt; eq(Eq&lt;A&gt; eqA) {\n    return (d1, d2) -&gt;\n        eqA.eq(d1.fst(), d2.fst())\n        &amp;&amp; eqA.eq(d1.snd(), d2.snd());\n  }\n}</code></pre>\n<p>Let&#39;s unpack this:</p>\n<ul><li><code>Eq&lt;A&gt;</code> now compares two values of type <code>A</code>.<ul><li><p>This indicates that we are no longer expecting the implicit <code>this</code> parameter</p><p>to be relevant in equality.</p></li></ul></li><li><code>Point</code> no longer implements the interface.<ul><li>Now it provides a static constructor (factory) for <code>Eq&lt;Point&gt;</code>.</li><li>We call this a <em>witness</em> that <code>Point</code> conforms to <code>Eq</code>.</li></ul></li><li><code>Dup</code> also does not implement the interface.<ul><li>Its static constructor for <code>Eq&lt;Dup&lt;A&gt;&gt;</code> has a parameter of type <code>Eq&lt;A&gt;</code>.<ul><li>This indicates the dependency that we were looking for:</li><li>In order to prove <code>Eq&lt;Dup&lt;A&gt;&gt;</code>, we must first prove <code>Eq&lt;A&gt;</code>.</li></ul></li><li><p>It uses the witness <code>Eq&lt;A&gt; eqA</code>, which contains <code>boolean eq(A, A)</code>, to</p><p>implement its own intended behavior.</p></li></ul></li></ul>\n<p>The code is rather simple, but it marks a dramatic change of perspective:</p>\n<p>Equality now belongs to the type definition and not to an object instance.</p>\n<p>So, what are type classes?</p>\n<p>Type classes can be seen as a pattern that allows us to model shared behavior in\na way that puts types first AND is entirely compositional.</p>\n<p>Now, in the above example, how do we actually use <code>Dup</code>&#39;s <code>eq()</code>?</p>\n<pre><code>Eq&lt;Dup&lt;Point&gt;&gt; pointDupEq = Dup.eq(Point.eq());\n\npointDupEq.eq(new Point(1, 1), new Point(1, 1));\n// Returns: true</code></pre>\n<p>We must first construct the witness <code>Eq&lt;Dup&lt;Point&gt;&gt;</code> by chaining calls to the\nrespective <code>Eq</code> witness constructors of <code>Dup</code> and <code>Point</code>.</p>\n<p>Then, we use this witness to access/invoke the actual logic that we&#39;re after.</p>\n<p>Notice how this pattern composes very nicely:</p>\n<pre><code>static &lt;K, V&gt; Eq&lt;Map&lt;K, V&gt;&gt; mapEq(Eq&lt;K&gt; eqK, Eq&lt;V&gt; eqV) { ... }\n\nstatic &lt;E&gt; Eq&lt;List&lt;E&gt;&gt; listEq(Eq&lt;E&gt; eqE);\n\nstatic Eq&lt;Integer&gt; integerEq();\n\nstatic Eq&lt;String&gt; stringEq();\n\n// then:\n\nEq&lt;Map&lt;String, List&lt;Integer&gt;&gt;&gt; eq =\n  mapEq(stringEq(), listEq(integerEq()));\n\nMap&lt;String, List&lt;Integer&gt;&gt; m1 = ...;\nMap&lt;String, List&lt;Integer&gt;&gt; m2 = ...;\n\neq.eq(m1, m2);</code></pre>\n<p>With witness constructors as our building blocks, we can recursively construct\ninfinitely many witnesses, as needed.</p>\n<blockquote><p>A note on nomenclature:</p>\n<p>In Haskell, type class &#39;implementations&#39; are called <em>instances</em>.</p>\n<p>Here, I am calling them <em>witnesses</em> because that&#39;s how Java&#39;s Architect, Brian\nGoetz, decided to refer to them in\n<a href=\"https://www.youtube.com/watch?v=Gz7Or9C0TpM\" rel=\"nofollow ugc noopener\">a recent talk</a> he gave about\nbringing type classes to Java sometime in the future.</p></blockquote>\n<p>Now, wouldn&#39;t it be great if we didn&#39;t have to manually construct these\nwitnesses?</p>\n<h2 id=\"goal-programmatically-instantiating-witnesses\">Goal: Programmatically instantiating witnesses</h2>\n<p>Given:</p>\n<pre><code>@TypeClass\ninterface Show&lt;A&gt; {\n  String show(A value);\n\n  // Convenience shortcut\n  static &lt;A&gt; String show(Show&lt;A&gt; showA, A value) {\n    return showA.show(value);\n  }\n\n  @TypeClass.Witness\n  static Show&lt;Integer&gt; showInteger() { ... }\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;List&lt;A&gt;&gt; showList(Show&lt;A&gt; showA) { ... }\n}</code></pre>\n<p>Instead of:</p>\n<pre><code>Show&lt;List&lt;List&lt;Integer&gt;&gt;&gt; w =\n    Show.showList(Show.showList(Show.showInteger()));\n\nShow.show(w, List.of(List.of(1, 2), List.of(3, 4)));\n// Returns: &quot;[[1, 2], [3, 4]]&quot;</code></pre>\n<p>We want:</p>\n<pre><code>Show.show(witness(), List.of(List.of(1, 2), List.of(3, 4)));\n// Returns: &quot;[[1, 2], [3, 4]]&quot;</code></pre>\n<p>Which means that the <code>witness()</code> method is able to automatically construct the\nrequired witness for <code>Show&lt;List&lt;List&lt;Integer&gt;&gt;&gt;</code>.</p>\n<p>How do we get there?</p>\n<p>First, we must understand some aspects of how Java generics work at runtime.</p>\n<h2 id=\"aside-java-generics-and-type-erasure\">Aside: Java Generics and Type Erasure</h2>\n<p>It is a well-known fact that Java generics get erased at runtime.</p>\n<p>However, there are a few scenarios in which generics <em>do</em> get reified!</p>\n<blockquote><p>Note: &#39;Type Reification&#39; is the process by which type information is made\nconcrete and available at runtime.</p></blockquote>\n<p>Given:</p>\n<pre><code>interface Stream&lt;T&gt; { ... }\n\ninterface IntStream extends Stream&lt;Integer&gt; { ... }</code></pre>\n<p>Then:</p>\n<pre><code>IntStream.class.getInterfaces()[0];\n// Returns: Stream\n\nIntStream.class.getGenericInterfaces()[0];\n// Returns: Stream&lt;Integer&gt;</code></pre>\n<p>This means that Java <em>did</em> preserve the generic type arguments for the supertype\n<code>Stream&lt;Integer&gt;</code>. (We just need to know where to look for it.)</p>\n<p>Note that this also occurs with anonymous classes:</p>\n<pre><code>var s = new Stream&lt;String&gt;() { ... };\n\ns.getClass().getGenericInterfaces()[0];\n// Returns: Stream&lt;String&gt;</code></pre>\n<p>This leads us to our first workaround:</p>\n<h2 id=\"aside-capturing-static-types\">Aside: Capturing static types</h2>\n<p>We define:</p>\n<pre><code>interface Ty&lt;T&gt; {\n  default Type type() {\n    return requireNonNull(\n        ((ParameterizedType) getClass().getGenericInterfaces()[0])\n            .getActualTypeArguments()[0]);\n  }\n}</code></pre>\n<p>Which lets us write:</p>\n<pre><code>Type type = new Ty&lt;Map&lt;String, List&lt;Integer&gt;&gt;&gt;() {}.type();\n\nprintln(type);\n// Prints: Map&lt;String, List&lt;Integer&gt;&gt;</code></pre>\n<p>Why do we need this?</p>\n<p>Because our <code>witness()</code> method will need a runtime representation of the type\nthat it is trying to instantiate.</p>\n<p>So our use case has turned into:</p>\n<pre><code>Show.show(witness(new Ty&lt;&gt;() {}), List.of(List.of(1, 2), List.of(3, 4)));</code></pre>\n<p>Note that we do not have to specify the type argument for <code>Ty&lt;&gt;</code>. Thankfully,\ntype inference does that for us.</p>\n<p>Type inference works in this case because the <code>List.of(...)</code> parameter has a\nwell-defined type, and the call to <code>&lt;A&gt; Show.show(Show&lt;A&gt;, A)</code> lets type\ninference flow from the second argument to the first.</p>\n<h2 id=\"aside-understanding-java-lang-reflect-type-s-hierarchy\">Aside: Understanding <code>java.lang.reflect.Type</code>&#39;s hierarchy</h2>\n<p>The standard <code>java.lang.reflect.Type</code> hierarchy consists of:</p>\n<ul><li><code>Class&lt;?&gt;</code></li><li><code>GenericArrayType</code></li><li><code>ParameterizedType</code></li><li><code>TypeVariable&lt;?&gt;</code></li><li><code>WildcardType</code></li></ul>\n<p>Where:</p>\n<ul><li><code>Class&lt;?&gt;</code>, sometimes referred to as a &#39;raw type&#39;, represents either:<ul><li>A non-generic type like <code>String</code> or <code>Integer</code>.</li><li><p>A generic type like <code>Function&lt;T, R&gt;</code> where its type parameter list <code>[T, R]</code></p><p>can be retrieved via <code>TypeVariable&lt;?&gt;[] getTypeParameters()</code>.</p></li></ul></li><li><p><code>GenericArrayType</code> represents an array type <code>E[]</code> whose component type <code>E</code> is</p><p>a <code>ParameterizedType</code> or a <code>TypeVariable&lt;?&gt;</code>.</p></li><li><p><code>ParameterizedType</code> represents a type <code>T&lt;Arg1, Arg2, ..., ArgN&gt;</code>. Its</p><p><code>getRawType()</code> method returns the class or interface <code>T</code>.</p></li><li><p><code>TypeVariable&lt;?&gt;</code> represents an unbound type parameter of a generic</p><p>declaration like a generic class/interface or generic method.</p></li><li><code>WildcardType</code> represents an occurrence of <code>?</code> within a <code>ParameterizedType</code>.</li></ul>\n<p>Moreover, a <code>java.lang.reflect.Method</code> can be queried via\n<code>TypeVariable&lt;?&gt;[] getTypeParameters()</code>, <code>Type[] getGenericParameterTypes()</code>,\nand <code>Type getGenericReturnType()</code>.</p>\n<p>For example:</p>\n<pre><code>class Example {\n  static &lt;A&gt; List&lt;A&gt; hello(String s, A[] arr, Optional&lt;?&gt; opt) { ... }\n}</code></pre>\n<p>Inspecting the <code>Example</code>&#39;s <code>hello</code> method at runtime would yield something like:</p>\n<pre><code>Method(\n  name: &quot;hello&quot;,\n  typeParameters:\n    [TypeVariable(A)],\n  genericReturnType:\n    ParameterizedType(\n      rawType: List.class,\n      typeArguments: [TypeVariable(A)],\n    ),\n  genericParameterTypes:\n    [\n      String.class,\n      GenericArrayType(componentType: TypeVariable(A)),\n      ParameterizedType(\n        rawType: Optional.class,\n        typeArguments: [WildcardType()]\n      )\n    ]\n)</code></pre>\n<p>Note that the three ocurrences of <code>TypeVariable(A)</code> are unique in the sense that\nthey compare <code>equal()</code> to each other, but <em>not</em> to other type variable\ninstances, even if their names are also <code>A</code>.</p>\n<h2 id=\"subgoal-parsing-java-lang-reflect-type-into-an-ast\">Subgoal: Parsing <code>java.lang.reflect.Type</code> into an AST</h2>\n<p>We want to do this for a few reasons:</p>\n<ul><li><code>java.lang.reflect.Type</code> is not convenient for programming:<ul><li>It is not a sealed hierarchy, so pattern-matching on it is error-prone.</li><li>Both <code>Class&lt;?&gt;</code> and <code>GenericArrayType</code> may be array types.</li><li><p><code>Class&lt;?&gt;</code> may represent a primitive type, like <code>int</code> or <code>float</code>, which does</p><p>not participate in generic parameterization (yet).</p></li><li><p>Type application as represented by <code>ParameterizedType</code> is variadic.</p><p>Single-parameter-a-time is easier to program against.</p></li></ul></li><li><p>When we get to higher-kinded type classes (spoiler!), then we will need our</p><p>own type representation anyway.</p></li></ul>\n<p>Here&#39;s the AST:</p>\n<pre><code>sealed interface ParsedType {\n  record Var(TypeVariable&lt;?&gt; java) implements ParsedType {}\n\n  record App(ParsedType fun, ParsedType arg) implements ParsedType {}\n\n  record ArrayOf(ParsedType elementType) implements ParsedType {}\n\n  record Const(Class&lt;?&gt; java) implements ParsedType {}\n\n  record Primitive(Class&lt;?&gt; java) implements ParsedType {}\n}</code></pre>\n<p>And these are our parsing rules:</p>\n<pre><code>sealed interface ParsedType {\n  // ...\n\n  static ParsedType parse(Type java) {\n    return switch (java) {\n      case Class&lt;?&gt; arr when arr.isArray() -&gt;\n          new ArrayOf(parse(arr.getComponentType()));\n      case Class&lt;?&gt; prim when prim.isPrimitive() -&gt;\n          new Primitive(prim);\n      case Class&lt;?&gt; c -&gt;\n          new Const(c);\n      case TypeVariable&lt;?&gt; v -&gt;\n          new Var(v);\n      case ParameterizedType p -&gt;\n          parseAll(p.getActualTypeArguments()).stream()\n              .reduce(parse(p.getRawType()), App::new);\n      case GenericArrayType a -&gt;\n          new ArrayOf(parse(a.getGenericComponentType()));\n      case WildcardType w -&gt;\n          throw new IllegalArgumentException(&quot;Cannot parse wildcard type: &quot; + w);\n      default -&gt;\n          throw new IllegalArgumentException(&quot;Unsupported type: &quot; + java);\n    };\n  }\n}</code></pre>\n<blockquote><p>Note: the rule for <code>ParameterizedType</code> will take a type like <code>T&lt;A, B&gt;</code> and\nturn it into <code>App(App(Const(T), A), B)</code>.</p>\n<p>This means that the generic type <code>T</code> is first applied to <code>A</code> and then to <code>B</code>.</p></blockquote>\n<p>For example, <code>Map&lt;Integer, List&lt;String&gt;&gt;</code> becomes:</p>\n<pre><code>App(\n  App(Const(Map.class), Const(Integer.class)),\n  App(Const(List.class), Const(String.class))\n)</code></pre>\n<p>That&#39;s it! Really, it&#39;s not much, but the added uniformity will help our code\ndown the line.</p>\n<h2 id=\"recap\">Recap</h2>\n<p>Until this point we have:</p>\n<ul><li><p><code>new Ty&lt;T&gt;() {}</code>: a way to capture a type <code>T</code> from a static context and access</p><p>it at runtime.</p></li><li><code>ParsedType</code>: a uniform representation for Java&#39;s <code>java.lang.reflect.Type</code>s.</li></ul>\n<p>Now, let&#39;s move on to the problem of type class instance resolution.</p>\n<h2 id=\"subgoal-witness-resolution\">Subgoal: Witness resolution</h2>\n<p>Let&#39;s consider the following scenario:</p>\n<pre><code>@TypeClass\ninterface Show&lt;A&gt; {\n  String show(A value);\n\n  // Convenience shortcut\n  static &lt;A&gt; String show(Show&lt;A&gt; showA, A value) {\n    return showA.show(value);\n  }\n\n  @TypeClass.Witness\n  static Show&lt;Integer&gt; showInteger() { ... }\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;List&lt;A&gt;&gt; showList(Show&lt;A&gt; showA) { ... }\n}\n\nrecord Pair&lt;A, B&gt;(A fst, B snd) {\n  @TypeClass.Witness\n  public static &lt;A, B&gt; Show&lt;Pair&lt;A, B&gt;&gt; show(Show&lt;A&gt; showA, Show&lt;B&gt; showB) { ... }\n}</code></pre>\n<p>We observe that, for example, in order to summon a witness for\n<code>Show&lt;List&lt;Pair&lt;Integer, List&lt;Integer&gt;&gt;&gt;</code>, then we must apply several witness\nconstructors recursively.</p>\n<blockquote><p>What is a witness constructor? It is a <code>public static</code> method annotated with\n<code>@TypeClass.Witness</code>.</p></blockquote>\n<p>But first, we must find the witness constructors!</p>\n<p>How do we do that? Do we need to do a runtime scan of all loaded classes?</p>\n<p>That would be way too complicated.</p>\n<p>In order to reduce our search scope, we can define the following convention:</p>\n<blockquote><p>When trying to resolve a witness <code>C&lt;T&gt;</code> for some type class <code>C</code> and a concrete\ntype <code>T</code>, then we only look for witness constructors within the definitions of\n<code>C</code> and <code>T</code> and nowhere else.</p></blockquote>\n<p>For example, when looking for <code>Show&lt;Integer&gt;</code>, then we scan the methods of\n<code>Show</code> and <code>Integer</code>.</p>\n<p>Generally, we will prefer to define witness constructors within concrete types\nand not within type classes. However, for a built-in type like <code>Integer</code>, we\nhave no choice and we must define its witness constructors within the relevant\ntype classes, as we have done with <code>static Show&lt;Integer&gt; showInteger()</code>.</p>\n<p>Now that we are able to find the relevant witness constructors, how do we:</p>\n<ul><li>know which of them we must invoke,</li><li>and in <em>which order</em>?</li></ul>\n<p>First, we must understand type unification:</p>\n<h2 id=\"aside-type-unification\">Aside: Type Unification</h2>\n<p>Type unification is the process of finding a substitution that makes two types\nidentical.</p>\n<p>For example, given <code>Pair&lt;[A], String&gt;</code> and <code>Pair&lt;Integer, String&gt;</code>, then the\nsubstitution <code>{A -&gt; Integer}</code> would make the first type identical to the second.</p>\n<blockquote><p>Here, I have placed the type <code>A</code> between square brackets to indicate that it\nis a <em>type variable</em>. We only substitute type variables!</p></blockquote>\n<p>Unification may fail when it encounters incompatible types. For example,\n<code>List&lt;Integer&gt;</code> and <code>List&lt;String&gt;</code> cannot be unified because no substitution\nexists that would make them identical.</p>\n<p>Conversely, when two types are already identical, unification succeeds with an\nempty substitution <code>{}</code>.</p>\n<p>Because types are recursive, the unification algorithm must also be recursive\n(in nature).</p>\n<p>Consider:</p>\n<pre><code>unify(\n  Map&lt;Pair&lt;String, [T]&gt;, List&lt;[U]&gt;&gt;,\n  Map&lt;Pair&lt;String, Integer&gt;, List&lt;Optional&lt;Boolean&gt;&gt;&gt;\n)\n\n// we are unifying two types of shape:\n// - Map&lt;K1, V1&gt;\n// - Map&lt;K2, V2&gt;\n// the type constructors match on either side (Map)\n// so we recurse to their type arguments:\n// unify(K1, K2) ∪ unify(V1, V2)\n// and so on!\n\n=   unify(Pair&lt;String, [T]&gt;, Pair&lt;String, Integer&gt;)\n  ∪ unify(List&lt;[U]&gt;, List&lt;Optional&lt;Boolean&gt;&gt;)\n\n=   unify(String, String) // first arguments of Pair\n  ∪ unify([T], Integer)   // second arguments of Pair\n  ∪ unify([U], &lt;Optional&lt;Boolean&gt;) // arguments of List\n\n=   {}\n  ∪ {T -&gt; Integer}\n  ∪ {U -&gt; Optional&lt;Boolean&gt;}\n\n= {T -&gt; Integer, U -&gt; Optional&lt;Boolean&gt;}\n\n// Done!</code></pre>\n<p>If any unification step fails, then the entire unification fails:</p>\n<pre><code>unify(Pair&lt;[T], Integer&gt;, Pair&lt;String, String&gt;)\n\n=   unify([T], String)\n  ∪ unify(Integer, String)\n\n=   {T -&gt; String}\n  ∪ err\n\n= err</code></pre>\n<p>Following is a code listing for the concrete type unification algorithm\n<code>unify()</code> for our <code>ParsedType</code> definition, along with a <code>subsitute()</code> method\nthat applies a substitution to a type.</p>\n<h3 id=\"listing-type-unification-algorithm-for-parsedtype\">Listing: Type Unification algorithm for <code>ParsedType</code></h3>\n<p>Here we use the <code>Maybe</code> type, which is analogous to Java&#39;s <code>Optional</code>. It is\nused to indicate that the <code>unify()</code> function may fail with a <code>nothing()</code> (empty)\ncase. This is different from it returning <code>just(Map.of())</code> (an empty map), which\nsignals success but without any substitutions. In short, based on the examples\nabove, <code>err</code> is <code>nothing()</code> and <code>{}</code> is <code>just(Map.of())</code>.</p>\n<p>A key step here is the unification of <code>App</code>. This is the main driver for the\nalgorithm&#39;s recursion.</p>\n<p>The following helper methods are relevant to this step:</p>\n<pre><code>// applies function `f` if both `ma` and `mb` are present\n&lt;A, B, C&gt; Maybe&lt;C&gt; apply(BiFunction&lt;A, B, C&gt; f, Maybe&lt;A&gt; ma, Maybe&lt;B&gt; mb);\n\n// merges two maps\n// it fails at runtime if duplicate keys /with different values/ exist\n&lt;K, V&gt; Map&lt;K, V&gt; merge(Map&lt;K, V&gt; m1, Map&lt;K, V&gt; m2);</code></pre>\n<p>Here&#39;s the full code:</p>\n<pre><code>class Unification {\n  public static Maybe&lt;Map&lt;ParsedType.Var, ParsedType&gt;&gt; unify(ParsedType t1, ParsedType t2) {\n    return switch (Pair.of(t1, t2)) {\n      case Pair&lt;ParsedType, ParsedType&gt;(ParsedType.Var var1, ParsedType.Primitive p) -&gt;\n          Maybe.nothing(); // no primitives in generics\n      case Pair&lt;ParsedType, ParsedType&gt;(ParsedType.Var var1, var t) -&gt;\n          Maybe.just(Map.of(var1, t));\n      case Pair&lt;ParsedType, ParsedType&gt;(ParsedType.Const const1, ParsedType.Const const2)\n          when const1.equals(const2) -&gt;\n          Maybe.just(Map.of());\n      case Pair&lt;ParsedType, ParsedType&gt;(\n              ParsedType.App(var fun1, var arg1),\n              ParsedType.App(var fun2, var arg2)) -&gt;\n          Maybe.apply(Maps::merge, unify(fun1, fun2), unify(arg1, arg2));\n      case Pair&lt;ParsedType, ParsedType&gt;(\n              ParsedType.ArrayOf(var elem1),\n              ParsedType.ArrayOf(var elem2)) -&gt;\n          unify(elem1, elem2);\n      case Pair&lt;ParsedType, ParsedType&gt;(\n              ParsedType.Primitive(var prim1),\n              ParsedType.Primitive(var prim2))\n          when prim1.equals(prim2) -&gt;\n          Maybe.just(Map.of());\n      default -&gt;\n          Maybe.nothing();\n    };\n  }\n\n  public static ParsedType substitute(Map&lt;ParsedType.Var, ParsedType&gt; map, ParsedType type) {\n    return switch (type) {\n      case ParsedType.Var var -&gt;\n          map.getOrDefault(var, var);\n      case ParsedType.App(var fun, var arg) -&gt;\n          new ParsedType.App(substitute(map, fun), substitute(map, arg));\n      case ParsedType.ArrayOf var -&gt;\n          new ParsedType.ArrayOf(substitute(map, var.elementType()));\n      case ParsedType.Primitive p -&gt;\n          p;\n      case ParsedType.Const c -&gt;\n          c;\n    };\n  }\n\n  public static List&lt;ParsedType&gt; substituteAll(\n      Map&lt;ParsedType.Var, ParsedType&gt; map, List&lt;ParsedType&gt; types) {\n    return types.stream().map(t -&gt; substitute(map, t)).toList();\n  }\n}</code></pre>\n<h3 id=\"extra-a-type-representation-for-static-methods\">Extra: A type representation for static methods</h3>\n<pre><code>record FuncType(Method java, List&lt;ParsedType&gt; paramTypes, ParsedType returnType) {\n  public static FuncType parse(Method method) {\n    if (!Modifier.isStatic(method.getModifiers())) {\n      throw new IllegalArgumentException(&quot;Method must be static: &quot; + method);\n    }\n    return new FuncType(\n        method,\n        ParsedType.parseAll(method.getGenericParameterTypes()),\n        ParsedType.parse(method.getGenericReturnType()));\n  }\n}</code></pre>\n<h2 id=\"subgoal-witness-resolution-part-2\">Subgoal: Witness resolution, part 2</h2>\n<p>Why is type unification relevant?</p>\n<p>Type unification will guide the witness resolution process in two ways:</p>\n<ul><li>By checking if any given witness constructor is relevant to our witness goal.</li><li><p>And if it <em>is</em> relevant, then it will tell us which witness subgoals to</p><p>resolve next.</p></li></ul>\n<p>For example, when resolving <code>Show&lt;List&lt;Integer&gt;&gt;</code>, we check:</p>\n<ol><li>Which witness constructors can we find?</li></ol>\n<ul><li>We scan <code>List</code>:<ul><li>It contains zero witness constructors.</li></ul></li><li>We scan <code>Show</code>, and it contains:<ul><li><code>Show&lt;Integer&gt; showInteger()</code></li><li><code>&lt;A&gt; Show&lt;List&lt;A&gt;&gt; showList(Show&lt;A&gt; showA)</code></li></ul></li></ul>\n<ol start=\"2\"><li>Which witness constructors apply to our goal?</li></ol>\n<ul><li>Does <code>Show&lt;Integer&gt; showInteger()</code> apply?<ul><li>We try to unify <code>Show&lt;Integer&gt;</code> and <code>Show&lt;List&lt;Integer&gt;&gt;</code>.</li><li>Unification fails.</li><li>Skip this constructor ❌</li></ul></li><li>Does <code>&lt;A&gt; Show&lt;List&lt;A&gt;&gt; showList(Show&lt;A&gt; showA)</code> apply?<ul><li>We try to unify <code>Show&lt;List&lt;A&gt;&gt;</code> and <code>Show&lt;List&lt;Integer&gt;&gt;</code>.</li><li>Unification succeeds with the substitution <code>{A -&gt; Integer}</code>.</li><li>We can use this constructor ✅</li><li>But this constructor has an argument: <code>Show&lt;A&gt;</code></li><li>If we apply the substitution to the argument, we find our next goal:<ul><li><code>substitute({A -&gt; Integer}, Show&lt;A&gt;)</code> yields <code>Show&lt;Integer&gt;</code>.</li></ul></li><li>Add <code>Show&lt;Integer&gt;</code> to our goals.</li></ul></li></ul>\n<ol start=\"3\"><li>Recurse until we have no further goals.</li></ol>\n<p>That outlines the witness resolution algorithm.</p>\n<h2 id=\"recap-2\">Recap</h2>\n<p>Until this point we have:</p>\n<ul><li><p><code>new Ty&lt;T&gt;() {}</code>: a way to capture a type <code>T</code> from a static context and access</p><p>it at runtime.</p></li><li><code>ParsedType</code>: a uniform representation for Java&#39;s <code>java.lang.reflect.Type</code>s.</li><li><code>Unification</code>: an algorithm for type unification of <code>ParsedType</code>s.</li><li>And a rough sketch for the overall recursive witness resolution algorithm.</li></ul>\n<p>Now, let&#39;s put it all together!</p>\n<h2 id=\"subgoal-witness-resolution-implementation\">Subgoal: Witness resolution implementation</h2>\n<p>Let&#39;s start with the witness constructor lookup code:</p>\n<pre><code>@Retention(RetentionPolicy.RUNTIME)\n@interface TypeClass {\n  @Retention(RetentionPolicy.RUNTIME)\n  @interface Witness {}\n}\n\nclass TypeClasses {\n  // ...\n\n  private static List&lt;InstanceConstructor&gt; findRules(ParsedType target) {\n    return switch (target) {\n      case ParsedType.App(var fun, var arg) -&gt;\n          Lists.concat(findRules(fun), findRules(arg));\n      case ParsedType.Const(var java) -&gt;\n          rulesOf(java);\n      case ParsedType.Var(var java) -&gt;\n          List.of();\n      case ParsedType.ArrayOf(var elem) -&gt;\n          List.of();\n      case ParsedType.Primitive(var java) -&gt;\n          List.of();\n    };\n  }\n\n  private static List&lt;InstanceConstructor&gt; rulesOf(Class&lt;?&gt; cls) {\n    return Arrays.stream(cls.getDeclaredMethods())\n        .filter(TypeClasses::isWitnessMethod)\n        .map(FuncType::parse)\n        .map(InstanceConstructor::new)\n        .toList();\n  }\n\n  private static boolean isWitnessMethod(Method m) {\n    return m.accessFlags().contains(PUBLIC)\n        &amp;&amp; m.accessFlags().contains(STATIC)\n        &amp;&amp; m.isAnnotationPresent(TypeClass.Witness.class);\n  }\n\n  private record InstanceConstructor(FuncType func) {}\n\n  // ...\n}</code></pre>\n<p>Now, let&#39;s move on to how we choose relevant witness constructors and find our\nsubsequent goals:</p>\n<pre><code>class TypeClasses {\n  // ...\n\n  private static List&lt;Candidate&gt; findCandidates(ParsedType target) {\n    return findRules(target).stream()\n        .flatMap(\n            rule -&gt;\n                rule\n                    .tryMatch(target)\n                    .map(requirements -&gt; new Candidate(rule, requirements))\n                    .stream())\n        .toList();\n  }\n\n  private record Candidate(WitnessRule rule, List&lt;ParsedType&gt; requirements) {}\n\n  // Spoiler: we will have another subtype of WitnessRule later on (;\n  private sealed interface WitnessRule {\n    Maybe&lt;List&lt;ParsedType&gt;&gt; tryMatch(ParsedType target);\n\n    Object instantiate(List&lt;Object&gt; dependencies);\n  }\n\n  private record InstanceConstructor(FuncType func) implements WitnessRule {\n    @Override\n    public Maybe&lt;List&lt;ParsedType&gt;&gt; tryMatch(ParsedType target) {\n      return Unification.unify(func.returnType(), target)\n          .map(map -&gt; Unification.substituteAll(map, func.paramTypes()));\n    }\n\n    @Override\n    public Object instantiate(List&lt;Object&gt; dependencies) {\n      try {\n        return func.java().invoke(null, dependencies.toArray());\n      } catch (Exception e) {\n        throw new RuntimeException(e);\n      }\n    }\n  }\n\n  // ...\n}</code></pre>\n<p>Then, the code that drives the recursive resolution:</p>\n<pre><code>class TypeClasses {\n  // ...\n\n  private static Either&lt;SummonError, Object&gt; summon(ParsedType target) {\n    return switch (ZeroOneMore.of(findCandidates(target, context))) {\n      case ZeroOneMore.One&lt;Candidate&gt;(Candidate(var rule, var requirements)) -&gt;\n          summonAll(requirements, context)\n              .map(rule::instantiate)\n              .mapLeft(error -&gt; new SummonError.Nested(target, error));\n      case ZeroOneMore.Zero&lt;Candidate&gt;() -&gt;\n          Either.left(new SummonError.NotFound(target));\n      case ZeroOneMore.More&lt;Candidate&gt;(var candidates) -&gt;\n          Either.left(new SummonError.Ambiguous(target, candidates));\n    };\n  }\n\n  private static Either&lt;SummonError, List&lt;Object&gt;&gt; summonAll(List&lt;ParsedType&gt; targets) {\n    return Either.traverse(targets, TypeClasses::summon);\n  }\n\n  private sealed interface SummonError {\n    record NotFound(ParsedType target) implements SummonError {}\n\n    record Ambiguous(ParsedType target, List&lt;Candidate&gt; candidates) implements SummonError {}\n\n    record Nested(ParsedType target, SummonError cause) implements SummonError {}\n  }\n\n  // ...\n}</code></pre>\n<p>And, finally, the public entry point for all of it:</p>\n<pre><code>class TypeClasses {\n  public static &lt;T&gt; T witness(Ty&lt;T&gt; ty) {\n    return switch (summon(ParsedType.parse(ty.type()))) {\n      case Either.Left&lt;SummonError, Object&gt;(SummonError error) -&gt;\n          throw new WitnessResolutionException(error);\n      case Either.Right&lt;SummonError, Object&gt;(Object instance) -&gt; {\n        @SuppressWarnings(&quot;unchecked&quot;)\n        T typedInstance = (T) instance;\n        yield typedInstance;\n      }\n    };\n  }\n\n  public static class WitnessResolutionException extends RuntimeException {\n    private WitnessResolutionException(SummonError error) {\n      super(error.format());\n    }\n  }\n\n  // ...\n}</code></pre>\n<p>Surprinsingly, that&#39;s it!</p>\n<p>You can find a <em>mostly accurate</em> compilation of all of the above code in\n<a href=\"https://gist.github.com/Garciat/a6ca3c9195d5b1d997badecd73282e38\" rel=\"nofollow ugc noopener\">this Gist</a>.\n(This was the first version of the type class system that I built.)</p>\n<p>Now, let&#39;s see it in action.</p>\n<h2 id=\"examples-first-order-type-classes-instances\">Examples: First-Order Type Classes &amp; Instances</h2>\n<h3 id=\"type-class-show\">Type Class: Show</h3>\n<p>Given:</p>\n<pre><code>@TypeClass\ninterface Show&lt;A&gt; {\n  String show(A a);\n\n  static &lt;A&gt; String show(Show&lt;A&gt; showA, A a) {\n    return showA.show(a);\n  }\n\n  @TypeClass.Witness\n  static Show&lt;Integer&gt; integerShow() {\n    return i -&gt; Integer.toString(i);\n  }\n\n  @TypeClass.Witness\n  static Show&lt;String&gt; stringShow() {\n    return s -&gt; &quot;\\&quot;&quot; + s + &quot;\\&quot;&quot;;\n  }\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;Optional&lt;A&gt;&gt; optionalShow(Show&lt;A&gt; showA) {\n    return optA -&gt; optA.map(a -&gt; &quot;Some(&quot; + showA.show(a) + &quot;)&quot;).orElse(&quot;None&quot;);\n  }\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;List&lt;A&gt;&gt; listShow(Show&lt;A&gt; showA) { ... }\n\n  @TypeClass.Witness\n  static &lt;K, V&gt; Show&lt;Map&lt;K, V&gt;&gt; mapShow(Show&lt;K&gt; showK, Show&lt;V&gt; showV) { ... }\n}</code></pre>\n<p>Then:</p>\n<pre><code>Map&lt;String, List&lt;Optional&lt;Integer&gt;&gt;&gt; m1 =\n    Map.of(\n        &quot;a&quot;, List.of(Optional.of(1), Optional.empty()),\n        &quot;b&quot;, List.of(Optional.of(2), Optional.of(3)));\n\nprintln(Show.show(witness(new Ty&lt;&gt;() {}), m1));\n// Prints: {&quot;a&quot;: [Some(1), None], &quot;b&quot;: [Some(2), Some(3)]}</code></pre>\n<p>Note that this requires the recursive instantiation of 5 witness constructors.\nHere&#39;s what some debug logs show:</p>\n<pre><code>Instantiating: () -&gt; Show[A](String)\nInstantiating: () -&gt; Show[A](Integer)\nInstantiating: ∀ A. (Show[A](A)) -&gt; Show[A](Optional[T](A))\nInstantiating: ∀ A. (Show[A](A)) -&gt; Show[A](List[E](A))\nInstantiating: ∀ K V. (Show[A](K), Show[A](V)) -&gt; Show[A](Map[K, V](K)(V))</code></pre>\n<h3 id=\"type-class-monoid-num\">Type Class: Monoid &amp; Num</h3>\n<p>Given:</p>\n<pre><code>@TypeClass\ninterface Monoid&lt;A&gt; {\n  A combine(A a1, A a2);\n\n  A identity();\n\n  static &lt;A&gt; A combineAll(Monoid&lt;A&gt; monoid, List&lt;A&gt; elements) {\n    A result = monoid.identity();\n    for (A element : elements) {\n      result = monoid.combine(result, element);\n    }\n    return result;\n  }\n}\n\n@TypeClass\ninterface Num&lt;A&gt; {\n  A add(A a1, A a2);\n\n  A mul(A a1, A a2);\n\n  A zero();\n\n  A one();\n\n  @TypeClass.Witness\n  static Num&lt;Integer&gt; integerNum() {\n    return new Num&lt;&gt;() {\n      @Override\n      public Integer add(Integer a1, Integer a2) {\n        return a1 + a2;\n      }\n\n      @Override\n      public Integer mul(Integer a1, Integer a2) {\n        return a1 * a2;\n      }\n\n      @Override\n      public Integer zero() {\n        return 0;\n      }\n\n      @Override\n      public Integer one() {\n        return 1;\n      }\n    };\n  }\n}\n\nrecord Sum&lt;A&gt;(A value) {\n  @TypeClass.Witness\n  public static &lt;A&gt; Monoid&lt;Sum&lt;A&gt;&gt; monoid(Num&lt;A&gt; num) {\n    return new Monoid&lt;&gt;() {\n      @Override\n      public Sum&lt;A&gt; combine(Sum&lt;A&gt; s1, Sum&lt;A&gt; s2) {\n        return new Sum&lt;&gt;(num.add(s1.value(), s2.value()));\n      }\n\n      @Override\n      public Sum&lt;A&gt; identity() {\n        return new Sum&lt;&gt;(num.zero());\n      }\n    };\n  }\n}</code></pre>\n<p>Then:</p>\n<pre><code>var sums = List.of(new Sum&lt;&gt;(3), new Sum&lt;&gt;(5), new Sum&lt;&gt;(10));\n\nprintln(Monoid.combineAll(witness(new Ty&lt;&gt;() {}), sums));\n// Prints: Sum[value=18]</code></pre>\n<h3 id=\"type-class-printall\">Type Class: PrintAll</h3>\n<p>This example is based on:\n<a href=\"https://wiki.haskell.org/Varargs\" rel=\"nofollow ugc noopener\">https://wiki.haskell.org/Varargs</a></p>\n<p>It abuses type classes in order to implement variadic functions. Please read the\nlink above to understand how this works.</p>\n<p>It&#39;s really striking how it <em>just works</em> with our system!</p>\n<p>Given:</p>\n<pre><code>@TypeClass\ninterface PrintAll&lt;T&gt; {\n  T printAll(List&lt;String&gt; strings);\n\n  static &lt;T&gt; T of(PrintAll&lt;T&gt; printAll) {\n    return printAll.printAll(List.of());\n  }\n\n  @TypeClass.Witness\n  static PrintAll&lt;Void&gt; base() {\n    return strings -&gt; {\n      for (String s : strings) {\n        System.out.println(s);\n      }\n      return null;\n    };\n  }\n\n  @TypeClass.Witness\n  static &lt;A, R&gt; PrintAll&lt;Function&lt;A, R&gt;&gt; func(Show&lt;A&gt; showA, PrintAll&lt;R&gt; printR) {\n    return strings -&gt; a -&gt; printR.printAll(Lists.concat(strings, List.of(showA.show(a))));\n  }\n}</code></pre>\n<p>Then:</p>\n<pre><code>Function&lt;String, Function&lt;List&lt;String&gt;, Function&lt;Integer, Void&gt;&gt;&gt; printer =\n    PrintAll.of(witness(new Ty&lt;&gt;() {}));\n\nprinter.apply(&quot;Items:&quot;).apply(JavaList.of(&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;)).apply(42);\n// Prints:\n// &quot;Items:&quot;\n// [&quot;apple&quot;, &quot;banana&quot;, &quot;cherry&quot;]\n// 42</code></pre>\n<h3 id=\"type-class-type-equality\">Type Class: Type Equality!</h3>\n<p>This examples shows how we can encode propositions about types themselves and\nhave the resolver construct evidence for them.</p>\n<p>Reified type equality is very neat construct that I&#39;d like to write more about\nsoon.</p>\n<p>For now, let&#39;s appreciate how this neatly encodes\n<a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Data-Type-Equality.html\" rel=\"nofollow ugc noopener\">Haskell&#39;s own type equality</a>\nin Java.</p>\n<p>Consider:</p>\n<pre><code>@TypeClass\nsealed interface TyEq&lt;A, B&gt; {\n  A castR(B b);\n\n  B castL(A a);\n\n  @TypeClass.Witness\n  static &lt;T&gt; TyEq&lt;T, T&gt; refl() {\n    return new Refl&lt;&gt;();\n  }\n\n  record Refl&lt;T&gt;() implements TyEq&lt;T, T&gt; {\n\n    @Override\n    public T castR(T t) {\n      return t;\n    }\n\n    @Override\n    public T castL(T t) {\n      return t;\n    }\n  }\n}</code></pre>\n<p><code>TyEq&lt;A, B&gt;</code> represents the proposition that <code>A = B</code>.</p>\n<p>And the only possible constructor for it is <code>refl()</code>, which can only prove that\n<code>TyEq&lt;T, T&gt;</code> for any <code>T</code>.</p>\n<p>How is that useful?</p>\n<p><code>TyEq&lt;T, T&gt;</code> may be trivial in the static context where it is constructed. But\nit certainly is not in the context where it may be needed.</p>\n<p>Take a look:</p>\n<pre><code>class List&lt;T&gt; {\n  // ...\n\n  int sum(TyEq&lt;T, Integer&gt; ty) {\n    int s = 0;\n    for (T item : this) {\n      s += ty.castL(item); // casts T to Integer !\n    }\n    return s;\n  }\n\n  // ...\n}\n\n// then\n\nvar xs = List.of(1, 2, 3);\n\nxs.sum(refl());\n// Returns: 6</code></pre>\n<p>Within <code>List&lt;T&gt;</code>, we have no idea what <code>T</code>. Code can make no assumptions about\nit. However, if we have a proof that <code>T = Integer</code>, then we <em>can</em> sum the\nelements up into an <code>int</code>.</p>\n<p>Now, we can also request them as witnesses in a witness constructor:</p>\n<pre><code>@TypeClass\ninterface SumAllInt&lt;A&gt; {\n  A sum(List&lt;Integer&gt; list);\n\n  static &lt;T&gt; T of(SumAllInt&lt;T&gt; sumAllInt) {\n    return sumAllInt.sum(List.of());\n  }\n\n  @TypeClass.Witness\n  static SumAllInt&lt;Integer&gt; base() {\n    return list -&gt; list.stream().mapToInt(Integer::intValue).sum();\n  }\n\n  @TypeClass.Witness\n  static &lt;A, R&gt; SumAllInt&lt;Function&lt;A, R&gt;&gt; func(TyEq&lt;A, Integer&gt; eq, SumAllInt&lt;R&gt; sumR) {\n    return list -&gt; a -&gt; sumR.sum(Lists.concat(list, List.of(eq.castL(a))));\n  }\n}</code></pre>\n<p>Similar to the <code>PrintAll</code> example, this lets us summon variadic functions:</p>\n<pre><code>Function&lt;Integer, Function&lt;Integer, Function&lt;Integer, Integer&gt;&gt;&gt; sum =\n    SumAllInt.of(witness(new Ty&lt;&gt;() {}));\nprintln(sum.apply(1).apply(2).apply(3));</code></pre>\n<p>However, notice how in the <code>func</code> rule, we requested <code>TyEq&lt;A, Integer&gt;</code>. This\nconstrains the <code>A</code> type argument to just <code>Integer</code>.</p>\n<p>Funny enough, this is actually only necessary in Haskell due to how integer\nliterals are overloaded. So I implemented this here for no gain. But it was cool\nthat it just worked!</p>\n<h2 id=\"goal-higher-order-type-classes\">Goal: Higher-Order Type Classes</h2>\n<p>Consider the <code>Functor</code> type class in Haskell:</p>\n<pre><code>class Functor f where\n  fmap :: (a -&gt; b) -&gt; f a - &gt; f b</code></pre>\n<p>Notice how <code>f</code> is not a type?</p>\n<p>It is not one because it is being applied to types <code>a</code> and <code>b</code>, respectively.</p>\n<p>This means that <code>f</code> is a <em>type constructor</em>.</p>\n<blockquote><p>A type constructor is a sort of type-level <em>function</em> that builds new types\nfrom existing types.</p>\n<p>Here, we mean <em>function</em> in the sense that it has the mathematical property of\nbeing <em>injective</em>. That is, no two inputs map to the same output.</p>\n<p>In practice, this means that if we know that the output is the same, then the\ninput must be the same:</p>\n<p><code>F&lt;X&gt; = F&lt;Y&gt; ⇒ X = Y</code></p>\n<p>This property is what allows us to unify under type constructors.</p></blockquote>\n<p>In Java, <code>class List&lt;T&gt;</code> can be seen as a type constructor. Though we don&#39;t use\ntype application syntax as in Haskell, we do use type parameterization syntax:\n<code>List&lt;Integer&gt;</code>. In Java, generic types cannot be partially applied. That is,\nfor a type <code>class C&lt;T1, T2, ..., TN&gt;</code>, we must provide all <code>N</code> type arguments at\nonce.</p>\n<p>Let&#39;s try representing the <code>Functor</code> type class with a Java interface:</p>\n<pre><code>interface Functor&lt;F&gt; {\n  &lt;A, B&gt; F&lt;B&gt; fmap(Function&lt;A, B&gt; f, F&lt;A&gt; fa);\n}</code></pre>\n<p>Uh-oh:</p>\n<pre><code>The type F is not generic; it cannot be parameterized with arguments &lt;B&gt;</code></pre>\n<p>Indeed, Java requires that the type parameter <code>F</code> is a <em>type</em>, and <strong>not a type\nconstructor</strong>.</p>\n<p>Are we cooked?</p>\n<p>Not quite.</p>\n<h2 id=\"aside-what-is-a-kind\">Aside: What is a kind?</h2>\n<p>Simply: values have types; types have kinds.</p>\n<p><code>42</code> has type <code>Integer</code>.</p>\n<p><code>Integer</code> has kind <code>*</code> (read: star).</p>\n<blockquote><p>The syntax <code>*</code> for the kind of plain types comes from Haskell.</p>\n<p>We could also say <code>Integer</code> has kind <code>Type</code>, but that may be confusing given\nhow overloaded the word &#39;type&#39; is in this context.</p></blockquote>\n<p><code>List&lt;Integer&gt;</code> also has kind <code>*</code>.</p>\n<p>So what is the kind of <code>List</code> itself?</p>\n<p><code>List</code> has kind <code>* -&gt; *</code>.</p>\n<p>That is, it is a type-level function that accepts a type and returns a type.</p>\n<h2 id=\"aside-higher-kinded-types-in-java\">Aside: Higher-Kinded Types in Java</h2>\n<p>Here we introduce an <em>encoding</em> of higher-kinded types for Java.</p>\n<p>Recall that interface <code>Functor&lt;F&gt;</code> failed because Java insists <code>F</code> is a concrete\ntype, not a type constructor. This encoding gives us a way to pretend <code>F</code> is\nhigher-kinded.</p>\n<blockquote><p>Note: I did not invent this encoding. I have seen several projects using it\n(see the Related Work section). I am not sure where it originated, but it is\nrather elegant.</p></blockquote>\n<p>Consider:</p>\n<pre><code>interface TApp&lt;F, A&gt; {}\n\nabstract class TagBase {}\n\nsealed interface Maybe&lt;A&gt; extends TApp&lt;Maybe.Tag, A&gt; {\n  record Nothing&lt;A&gt;() implements Maybe&lt;A&gt; {}\n  \n  record Just&lt;A&gt;(A value) implements Maybe&lt;A&gt; {}\n\n  final class Tag extends TagBase {}\n\n  static &lt;A&gt; Maybe&lt;A&gt; unwrap(TApp&lt;Maybe.Tag, A&gt; m) {\n    return (Maybe&lt;A&gt;) m; // unsafe if we misuse TApp and tag types\n  }\n}</code></pre>\n<p>Let&#39;s unpack this:</p>\n<ul><li><code>TApp&lt;F, A&gt;</code> represents type application, hence its name.<ul><li>We assume that <code>F</code> has kind <code>* -&gt; *</code> and <code>A</code> has kind <code>*</code>.</li><li><code>TApp&lt;F, A&gt;</code> represents <code>F&lt;A&gt;</code>.</li><li>Therefore <code>TApp&lt;F, A&gt;</code>, in principle, has kind <code>*</code>.</li></ul></li><li><p><code>Maybe.Tag</code> is a sort of proxy type for <code>Maybe</code> as an unapplied type</p><p>constructor.</p>\n<ul><li><code>TApp&lt;Maybe.Tag, A&gt;</code>, in principle, means <code>Maybe&lt;A&gt;</code>.</li><li>That is why <code>Maybe&lt;A&gt; extends TApp&lt;Maybe.Tag, A&gt;</code>.</li></ul></li><li><p><code>Maybe.Tag</code> extends <code>TagBase</code> just so that we can easily identify this type</p><p>via reflection later.</p></li></ul>\n<p>How is this useful?</p>\n<p>Now we <em>do</em> have a mechanism (more of a convention) to represent <code>Functor</code>!</p>\n<p>Check it out:</p>\n<pre><code>interface Functor&lt;F&gt; {\n  &lt;A, B&gt; TApp&lt;F, B&gt; fmap(Function&lt;A, B&gt; f, TApp&lt;F, A&gt; fa);\n}</code></pre>\n<p>Remember: <code>TApp&lt;F, A&gt;</code> means <code>F&lt;A&gt;</code>.</p>\n<p>And we can also define the witness constructor:</p>\n<pre><code>sealed interface Maybe&lt;A&gt; extends TApp&lt;Maybe.Tag, A&gt; {\n  // ...\n\n  default &lt;B&gt; Maybe&lt;B&gt; map(Function&lt;A, B&gt; f) { ... }\n\n  @TypeClass.Witness\n  static Functor&lt;Maybe.Tag&gt; functor() {\n    return new Functor&lt;&gt;() {\n      @Override\n      public &lt;A, B&gt; TApp&lt;Maybe.Tag, B&gt; fmap(Function&lt;A, B&gt; f, TApp&lt;Maybe.Tag, A&gt; fa) {\n        return unwrap(fa).map(f);\n      }\n    };\n  }\n}</code></pre>\n<p>Let&#39;s unpack that:</p>\n<ul><li>We define a witness constructor for <code>Functor&lt;Maybe.Tag&gt;</code>.<ul><li><p>Remember: <code>Functor&lt;Maybe.Tag&gt;</code> means <code>Functor&lt;Maybe&gt;</code>, where <code>Maybe</code> is the</p><p>unapplied type constructor.</p></li></ul></li><li>We then have to implement:<ul><li><code>&lt;A, B&gt; TApp&lt;Maybe.Tag, B&gt; fmap(Function&lt;A, B&gt; f, TApp&lt;Maybe.Tag, A&gt; fa)</code></li><li>And remember:<ul><li><code>TApp&lt;Maybe.Tag, A&gt;</code> means <code>Maybe&lt;A&gt;</code></li><li><code>TApp&lt;Maybe.Tag, B&gt;</code> means <code>Maybe&lt;B&gt;</code></li></ul></li><li>If we squint our eyes a bit, <em>it does make sense</em>.</li></ul></li><li>We use <code>unwrap()</code> to convert from <code>TApp&lt;Maybe.Tag, A&gt;</code> to <code>Maybe&lt;A&gt;</code>.</li><li><p><code>Maybe&lt;B&gt;</code> is a subtype of <code>TApp&lt;Maybe.Tag, B&gt;</code>, so the return type just</p><p>works.</p></li></ul>\n<p>That&#39;s it!</p>\n<p>Now, we must extend our type class system to understand these typing\nconventions.</p>\n<h2 id=\"subgoal-extending-parsedtype-to-support-hkts\">Subgoal: Extending <code>ParsedType</code> to support HKTs</h2>\n<p>This is actually not very involved. Check it out:</p>\n<pre><code>sealed interface ParsedType {\n  // ...\n\n  static ParsedType parse(Type java) {\n    return switch (java) {\n      // New:\n      case Class&lt;?&gt; tag\n        when parseTagType(tag) instanceof Maybe.Just&lt;Class&lt;?&gt;&gt;(var tagged) -&gt;\n          new Const(tagged);\n      // New:\n      case ParameterizedType p\n          when parseAppType(p)\n              instanceof Maybe.Just&lt;Pair&lt;Type, Type&gt;&gt;(Pair&lt;Type, Type&gt;(var fun, var arg)) -&gt;\n          new App(parse(fun), parse(arg));\n      // etc\n    };\n  }\n\n  private static Maybe&lt;Class&lt;?&gt;&gt; parseTagType(Class&lt;?&gt; c) {\n    return switch (c.getEnclosingClass()) {\n      case Class&lt;?&gt; enclosing when c.getSuperclass().equals(TagBase.class) -&gt;\n          Maybe.just(enclosing);\n      case null -&gt; Maybe.nothing();\n      default -&gt; Maybe.nothing();\n    };\n  }\n\n  private static Maybe&lt;Pair&lt;Type, Type&gt;&gt; parseAppType(ParameterizedType t) {\n    return switch (t.getRawType()) {\n      case Class&lt;?&gt; raw when raw.equals(TApp.class) -&gt;\n          Maybe.just(Pair.of(t.getActualTypeArguments()[0], t.getActualTypeArguments()[1]));\n      default -&gt; Maybe.nothing();\n    };\n  }\n}</code></pre>\n<p>We only had to add two new pattern-match cases to <code>parse</code>:</p>\n<ul><li><code>case Class&lt;?&gt; tag when parseTagType(tag) ... tagged</code><ul><li><code>-&gt; new Const(tagged)</code></li><li>This replaces any occurrence of <code>T.Tag</code> with <code>T</code> itself.</li></ul></li><li><code>case ParameterizedType p when parseAppType(p) ... (fun, arg)</code><ul><li><code>-&gt; new App(parse(fun), parse(arg))</code></li><li><p>This replaces any occurrence of <code>TApp&lt;F, A&gt;</code> with</p><p><code>new App(parse(F), parse(A))</code>.</p></li></ul></li></ul>\n<p>That&#39;s it!</p>\n<p>I was also surprised! The witness resolution code needs no changes whatsoever!</p>\n<p>Although, I was worried about potential bugs coming from misuses of <code>Tag</code>s and\n<code>TApp</code>. So I came up with a lightweight embedded kind-checking mechanism.</p>\n<h2 id=\"aside-kind-checking-java-embedded-hkts\">Aside: Kind-checking Java-embedded HKTs</h2>\n<p>Given:</p>\n<pre><code>interface Kind&lt;K extends Kind.Base&gt; {\n  sealed interface Base {}\n\n  // KStar = *\n  final class KStar implements Base {}\n\n  // KArr k = * -&gt; k\n  final class KArr&lt;K extends Base&gt; implements Base {}\n}\n\nabstract class TagBase&lt;K extends Kind.Base&gt; implements Kind&lt;K&gt; {}\n\n// Full application of a unary type constructor\n// TApp :: (* -&gt; *) -&gt; * -&gt; *\ninterface TApp&lt;Tag extends Kind&lt;KArr&lt;KStar&gt;&gt;, A&gt; extends Kind&lt;KStar&gt; {}\n\n// Partial application of a binary type constructor\n// TPar :: (* -&gt; * -&gt; *) -&gt; * -&gt; (* -&gt; *)\ninterface TPar&lt;Tag extends Kind&lt;KArr&lt;KArr&lt;KStar&gt;&gt;&gt;, A&gt; extends Kind&lt;KArr&lt;KStar&gt;&gt; {}</code></pre>\n<ul><li><p><code>TApp</code> now can only apply to tags of kind <code>* -&gt; *</code>, itself becoming a kind</p><p><code>*</code>.</p></li><li><code>TPar</code> applies to tags of kind <code>* -&gt; * -&gt; *</code>, itself becoming a kind <code>* -&gt; *</code>.</li><li>This gives us some rudimentary kind-checking in Java</li></ul>\n<p>Then:</p>\n<pre><code>sealed interface Maybe&lt;A&gt; extends TApp&lt;Maybe.Tag, A&gt; {\n  // ...\n\n  final class Tag extends TagBase&lt;KArr&lt;KStar&gt;&gt; {}\n\n  static &lt;A&gt; Maybe&lt;A&gt; unwrap(TApp&lt;Tag, A&gt; value) {\n    return (Maybe&lt;A&gt;) value;\n  }\n}</code></pre>\n<p>And also:</p>\n<pre><code>@FunctionalInterface\ninterface State&lt;S, A&gt; extends TApp&lt;TPar&lt;State.Tag, S&gt;, A&gt; {\n  // ...\n\n  @TypeClass.Witness\n  static &lt;S&gt; Functor&lt;TPar&lt;Tag, S&gt;&gt; functor() { ... }\n\n  final class Tag extends TagBase&lt;KArr&lt;KArr&lt;KStar&gt;&gt;&gt; {}\n\n  static &lt;S, A&gt; State&lt;S, A&gt; unwrap(TApp&lt;TPar&lt;Tag, S&gt;, A&gt; value) {\n    return (State&lt;S, A&gt;) value;\n  }\n}</code></pre>\n<p>Notice in this case that <code>State</code> has two type parameters:</p>\n<ul><li><code>State.Tag</code> has kind <code>KArr&lt;KArr&lt;KStar&gt;&gt;</code><ul><li>This means: <code>* -&gt; * -&gt; *</code></li></ul></li><li>The first application of <code>State.Tag</code> must go through <code>TPar</code>.</li><li>The subsequent application must go through <code>TApp</code>.</li></ul>\n<h2 id=\"examples-higher-kinded-type-clases\">Examples: Higher-Kinded Type Clases</h2>\n<h3 id=\"type-class-traversable\">Type Class: Traversable</h3>\n<p>According to Haskell&#39;s\n<a href=\"https://hackage.haskell.org/package/base-4.21.0.0/docs/Data-Traversable.html\" rel=\"nofollow ugc noopener\">documentation on Traversable</a>:</p>\n<blockquote><p>Traversable is the class of data structures that can be traversed from left to\nright, performing an action on each element.</p></blockquote>\n<p>The Haskell type class looks like this:</p>\n<pre><code>class (Functor t, Foldable t) =&gt; Traversable t where\n  traverse :: Applicative f =&gt; (a -&gt; f b) -&gt; t a -&gt; f (t b)</code></pre>\n<p>Notice the use of the following features:</p>\n<ul><li><code>Traversable</code> is higher-kinded.<ul><li>Its parameter <code>t</code> has kind <code>* -&gt; *</code>.</li></ul></li><li><code>Traversable</code> has two superclasses: <code>Functor</code> and <code>Foldable</code>.</li><li>Its method <code>traverse</code> is quantified on three type variables:<ul><li><code>a</code> and <code>b</code> are plain types, of kind <code>*</code>.</li><li><code>f</code> is of kind <code>* -&gt; *</code>, as it seen applied as <code>f b</code> and <code>f (t b)</code>.</li></ul></li><li>The type variable <code>f</code> has the constraint <code>Applicative f</code>.</li></ul>\n<p><code>Traversable</code> is a rather advanced type class.</p>\n<p>Let&#39;s translate it into Java.</p>\n<p>For now, let&#39;s ignore its superclasses:</p>\n<pre><code>@TypeClass\ninterface Traversable&lt;T extends Kind&lt;KArr&lt;KStar&gt;&gt;&gt; {\n\n  &lt;F extends Kind&lt;KArr&lt;KStar&gt;&gt;, A, B&gt;\n      TApp&lt;F, ? extends TApp&lt;T, B&gt;&gt; traverse(\n          Applicative&lt;F&gt; applicative,\n          Function&lt;A, ? extends TApp&lt;F, B&gt;&gt; f,\n          TApp&lt;T, A&gt; t);\n\n  // And a static helper, for convenience:\n  // (I omitted some types and arguments for brevity)\n  static &lt;F, A, B&gt; ... traverse(\n      Traversable&lt;T&gt; traversable,\n      Applicative&lt;F&gt; applicative,\n      ...) {\n    return traversable.traverse(applicative, f, t);\n  }\n}</code></pre>\n<p>We see that:</p>\n<ul><li><code>T</code> has kind <code>KArr&lt;KStar&gt;</code> which means <code>* -&gt; *</code>.</li><li>The method <code>traverse</code>:<ul><li>Is generic on three type variables: <code>F</code>, <code>A</code>, <code>B</code>.<ul><li><code>F</code> is also declared as having kind <code>* -&gt; *</code>.</li></ul></li><li>Depends on a witness of <code>Applicative&lt;F&gt;</code>.</li><li>Declares covariance <code>TApp&lt;T, B&gt;</code> on a couple of places:<ul><li><p>Because we know that we won&#39;t return values of static type <code>TApp&lt;T, B&gt;</code></p><p>itself, but rather values of type <code>T&lt;B&gt;</code>, which in our HKT convention will\nextend <code>TApp&lt;T, B&gt;</code>.</p></li></ul></li></ul></li></ul>\n<p>Then:</p>\n<pre><code>// Unfortunately for HKTs, we must wrap regular Java lists\nrecord JavaList&lt;A&gt;(List&lt;A&gt; toList) implements TApp&lt;JavaList.Tag, A&gt; {\n  // ...\n\n  @TypeClass.Witness\n  public static &lt;A&gt; Show&lt;JavaList&lt;A&gt;&gt; show(Show&lt;A&gt; showA) { ... }\n\n  @TypeClass.Witness\n  public static Functor&lt;JavaList.Tag&gt; functor() { ... }\n\n  @TypeClass.Witness\n  public static Traversable&lt;JavaList.Tag&gt; traversable() { ... }\n\n  // ...\n}\n\n// and:\n\nsealed interface Maybe&lt;A&gt; {\n  // ...\n\n  @TypeClass.Witness\n  static Applicative&lt;Maybe.Tag&gt; applicative() { ... }\n\n  // ...\n}</code></pre>\n<p>For example:</p>\n<pre><code>Traversable.traverse(\n    witness(new Ty&lt;&gt;() {}), // for Traversable&lt;JavaList&gt;\n    witness(new Ty&lt;&gt;() {}), // for Applicative&lt;Maybe&gt;\n    JavaList.of(1, 2, 3),\n    Maybe::just);\n// Returns: Just[value=JavaList[toList=[1, 2, 3]]]</code></pre>\n<h3 id=\"type-class-functor-applicative-monad\">Type Class: Functor, Applicative, Monad</h3>\n<p>As expected:</p>\n<pre><code>@TypeClass\ninterface Functor&lt;F extends Kind&lt;KArr&lt;KStar&gt;&gt;&gt; {\n  &lt;A, B&gt; TApp&lt;F, B&gt; map(Function&lt;A, B&gt; f, TApp&lt;F, A&gt; fa);\n}\n\n@TypeClass\ninterface Applicative&lt;F extends Kind&lt;KArr&lt;KStar&gt;&gt;&gt; extends Functor&lt;F&gt; {\n  &lt;A&gt; TApp&lt;F, A&gt; pure(A a);\n\n  &lt;A, B&gt; TApp&lt;F, B&gt; ap(TApp&lt;F, Function&lt;A, B&gt;&gt; ff, TApp&lt;F, A&gt; fa);\n\n  @Override\n  default &lt;A, B&gt; TApp&lt;F, B&gt; map(Function&lt;A, B&gt; f, TApp&lt;F, A&gt; fa) {\n    return ap(pure(f), fa);\n  }\n}\n\n@TypeClass\ninterface Monad&lt;M extends Kind&lt;KArr&lt;KStar&gt;&gt;&gt; extends Applicative&lt;M&gt; {\n  &lt;A, B&gt; TApp&lt;M, B&gt; flatMap(Function&lt;A, TApp&lt;M, B&gt;&gt; f, TApp&lt;M, A&gt; fa);\n\n  @Override\n  default &lt;A, B&gt; TApp&lt;M, B&gt; map(Function&lt;A, B&gt; f, TApp&lt;M, A&gt; fa) {\n    return flatMap(a -&gt; pure(f.apply(a)), fa);\n  }\n\n  @Override\n  default &lt;A, B&gt; TApp&lt;M, B&gt; ap(TApp&lt;M, Function&lt;A, B&gt;&gt; ff, TApp&lt;M, A&gt; fa) {\n    return flatMap(a -&gt; map(f -&gt; f.apply(a), ff), fa);\n  }\n}</code></pre>\n<p>I currently don&#39;t have any interesting <code>Monad</code> examples. I may add one or two\nsoon.</p>\n<h2 id=\"recap-3\">Recap</h2>\n<p>We have built:</p>\n<ul><li>A simple mechanism to capture static types for runtime access.</li><li>An AST and a parser for Java types.<ul><li>Including support for higher-kinded types encoded via &#39;tag types&#39;.</li></ul></li><li>Type unification on said AST of types.</li><li>A witness resolution algorithm that:<ul><li>For a witness type <code>C&lt;T&gt;</code>, finds witness constructors in <code>C</code> and <code>T</code>.<ul><li><p>A witness constructor is a public static method annotated with</p><p><code>@TypeClass.Witness</code>.</p></li></ul></li><li>Uses type unification to:<ul><li>Filter relevant witness constructors.</li><li>Guide the recursive search for witness dependencies.</li></ul></li></ul></li></ul>\n<p>And altogether:</p>\n<ul><li><p>We are able to resolve arbitrarily nested witnesses for first-order type</p><p>clases like <code>Eq</code>, <code>Ord</code>, and <code>Monoid</code> and also for higher-kinded type classes\nlike <code>Functor</code>, <code>Monad</code>, and <code>Traversable</code>.</p></li><li>The witness summoning syntax looks like:<ul><li><code>C&lt;T&gt; w = witness(new Ty&lt;&gt;() {});</code></li></ul></li><li><p>Witness resolution is currently done at runtime. Ideally, we should be able to</p><p>do this work at compile time, as that&#39;s where the power of type classes lies\nin languages like Haskell.</p></li></ul>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>This was <strong>a lot</strong> of fun to work on. I had never before implemented type\nclasses in a language, and it turned out to be way simpler than I thought. The\nkey insight was figuring out that type unification was all that I needed.\nStupidly, I did not check Haskell&#39;s spec to figure that out. But it did come\nsomewhat naturally after a bit of thinking, given my experience in implementing\ntype systems.</p>\n<p>This project was partially inspired by\n<a href=\"https://www.youtube.com/watch?v=Gz7Or9C0TpM\" rel=\"nofollow ugc noopener\">this recent Brian Goetz JVMLS talk</a>\nwhere he presents his early ideas on how to bring type clases to Java. His\nproposed syntax is <code>Show&lt;Integer&gt;.witness</code>. And, of course, the mechanism is\nsupposed to resolve witnesses at compile time.</p>\n<p>You can find the complete implementation in\n<a href=\"https://gist.github.com/Garciat/204226a528018fa7d10abb93fa51c4ca\" rel=\"nofollow ugc noopener\">this Gist</a>.</p>\n<p>The code also contains several other type class examples like QuickCheck&#39;s\n<a href=\"https://hackage-content.haskell.org/package/QuickCheck-2.17.1.0/docs/Test-QuickCheck-Arbitrary.html\" rel=\"nofollow ugc noopener\">Arbitrary type class</a>.</p>\n<h3 id=\"future-work\">Future work</h3>\n<ul><li>The resolution mechanism could use some caching:<ul><li>For witness constructor lookups.</li><li>For witness instances themselves.</li></ul></li><li>It would be ideal to shift witness resolution to compile time.<ul><li>Perhaps with a javac plugin or something like that?</li></ul></li></ul>\n<h3 id=\"related-work\">Related Work</h3>\n<p>Here are some related projects and posts:</p>\n<ul><li><a href=\"https://github.com/DanielGronau/highj\" rel=\"nofollow ugc noopener\">HighJ</a><ul><li>HKT encoding via <code>Higher&lt;F, A&gt;</code>.</li><li>Provides Functor/Monad/etc.</li><li><strong>No automatic instance resolution</strong>, instances are explicit classes.</li></ul></li><li><a href=\"https://github.com/aol/cyclops\" rel=\"nofollow ugc noopener\">Cyclops React</a><ul><li>Functional programming toolkit for Java.</li><li>Type class-like interfaces (Functor, Monad, etc.).</li><li><strong>Manual instance lookup</strong>, no reflection or unification.</li></ul></li><li><a href=\"https://github.com/higherkindness/higherkinded-j\" rel=\"nofollow ugc noopener\">Higher-Kinded-J</a><ul><li>Modern HKT encoding using witness types.</li><li>Supports Functor, Applicative, Monad, MonadError, etc.</li><li><strong>Instances must be explicitly provided</strong>, not inferred.</li></ul></li><li><a href=\"https://github.com/mduerig/java-functional\" rel=\"nofollow ugc noopener\">mduerig/java-functional</a><ul><li>Experimental HKT + type class encodings in Java.</li><li>Conceptually related exploration.</li><li><strong>No generic instance search or unification engine</strong>.</li></ul></li><li><a href=\"https://zarinfam.github.io/2018/12/01/type-classes-in-java/\" rel=\"nofollow ugc noopener\">Type Classes in Java (blog post)</a><ul><li>Demonstrates type class pattern using interfaces.</li><li><strong>Requires manual wiring of instances</strong>.</li></ul></li><li><a href=\"https://www.infosun.fim.uni-passau.de/cl/staff/sulzmann/javagi/\" rel=\"nofollow ugc noopener\">JavaGI</a><ul><li>Research extension to Java with Haskell-style type classes.</li><li>Uses <strong>unification-based instance resolution</strong>.</li><li><strong>Requires its own compiler</strong>, not a Java library.</li></ul></li></ul>\n<h2 id=\"annex-context-instances\">Annex: Context Instances</h2>\n<p>Consider this example:</p>\n<pre><code>static &lt;A&gt; String example(Show&lt;A&gt; showA, A value) {\n  return Show.show(witness(new Ty&lt;&gt;() {}), JavaList.of(value));\n}</code></pre>\n<p>It is equivalent to the following Haskell code:</p>\n<pre><code>example :: Show a =&gt; a -&gt; String\nexample value = show [value]</code></pre>\n<p>In the Java code, we try to lookup <code>Show&lt;List&lt;A&gt;&gt;</code>, but we don&#39;t know what <code>A</code>\nis!</p>\n<p>Sure, at runtime we may know its real type, but we actually would like\nresolution to be static. (Even though we use reflection for witness resolution.)</p>\n<p>In the Haskell code, the available instance of <code>Show a</code> as captured by the\nfunction&#39;s signature becomes available as a &#39;context instance&#39; that is used to\nderive <code>Show [a]</code> for the call of <code>show</code>.</p>\n<p>How can we achieve this in Java?</p>\n<p>First, we define a type that can capture a witness along with its static type:</p>\n<pre><code>abstract class Ctx&lt;T&gt; {\n  private final T instance;\n\n  Ctx(T instance) {\n    this.instance = instance;\n  }\n\n  public T instance() {\n    return instance;\n  }\n\n  public Type type() {\n    return requireNonNull(\n        ((ParameterizedType) getClass().getGenericSuperclass())\n            .getActualTypeArguments()[0]);\n  }\n}</code></pre>\n<p>It leverages the same mechanism as <code>Ty&lt;T&gt;</code> to capture static types.</p>\n<p>Then, we pass it to the <code>witness()</code> method:</p>\n<pre><code>static &lt;A&gt; String example(Show&lt;A&gt; showA, A value) {\n  return Show.show(\n      witness(new Ty&lt;&gt;() {}, new Ctx&lt;&gt;(showA) {}),\n      JavaList.of(value));\n}</code></pre>\n<p>Finally, we update a bit of our type class resolution code:</p>\n<pre><code>class TypeClasses {\n  // New: second parameter\n  public static &lt;T&gt; T witness(Ty&lt;T&gt; ty, Ctx&lt;?&gt;... context) {\n    return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) {\n      // ...\n    };\n  }\n\n  // New: parsing Ctx&lt;?&gt; into ContextInstance\n  private static List&lt;ContextInstance&gt; parseContext(Ctx&lt;?&gt;[] context) {\n    return Arrays.stream(context)\n        .map(ctx -&gt; new ContextInstance(ctx.instance(), ParsedType.parse(ctx.type())))\n        .toList();\n  }\n\n  // ...\n\n  // New: second parameter\n  private static Either&lt;SummonError, Object&gt; summon(\n      ParsedType target, List&lt;ContextInstance&gt; context) {\n    // ...\n  }\n\n  // New: second parameter\n  private static Either&lt;SummonError, List&lt;Object&gt;&gt; summonAll(\n      List&lt;ParsedType&gt; targets, List&lt;ContextInstance&gt; context) {\n    // ...\n  }\n\n  // New: second parameter\n  private static List&lt;Candidate&gt; findCandidates(\n      ParsedType target, List&lt;ContextInstance&gt; context) {\n    // New: use the context instances along with the discovered witness constructors!\n    return Stream.&lt;WitnessRule&gt;concat(\n            context.stream(),\n            findRules(target).stream())\n        .flatMap(...)\n        .toList();\n  }\n\n  // ...\n\n  // New: a new case class for WitnessRule\n  private record ContextInstance(Object instance, ParsedType type) implements WitnessRule {\n    @Override\n    public Maybe&lt;List&lt;ParsedType&gt;&gt; tryMatch(ParsedType target) {\n      // This is a concrete instance, so we only check for type equality\n      return target.equals(type) ? Maybe.just(List.of()) : Maybe.nothing();\n    }\n\n    @Override\n    public Object instantiate(List&lt;Object&gt; dependencies) {\n      // Trivial\n      return instance;\n    }\n  }\n}</code></pre>\n<p>That&#39;s all. A bit noisy, but rather simple.</p>\n<p>Now this works:</p>\n<pre><code>static &lt;A&gt; String example(Show&lt;A&gt; showA, A value) {\n  return Show.show(\n      witness(new Ty&lt;&gt;() {}, new Ctx&lt;&gt;(showA) {}),\n      JavaList.of(value));\n}\n\nprintln(example(witness(new Ty&lt;&gt;() {}), 123));</code></pre>\n<p>Here, the type captured by <code>new Ctx&lt;&gt;(showA) {}</code> is <code>Show&lt;A&gt;</code>, where <code>A</code> is the\n<em>unique type variable</em> belonging to the <code>example</code> method.</p>\n<p>The <code>Show&lt;List&lt;A&gt;&gt;</code> witness that we are trying to summon needs a <code>Show&lt;A&gt;</code> whose\ntype could only possibly exist in this static context!</p>\n<h2 id=\"annex-overlapping-instances\">Annex: Overlapping Instances</h2>\n<p>In Haskell, the <code>String</code> type is defined as:</p>\n<pre><code>type String = [Char]</code></pre>\n<p>That is, a <code>String</code> is just a list of <code>Char</code>.</p>\n<p>Now, notice the difference here:</p>\n<pre><code>show [1, 2, 3]\n-- &quot;[1, 2, 3]&quot;\n\nshow [(&#39;a&#39;, 1), (&#39;b&#39;, 2)]\n-- &quot;[(&#39;a&#39;, 1), (&#39;b&#39;, 2)]&quot;\n\nshow [&#39;a&#39;, &#39;b&#39;]\n-- &quot;\\&quot;ab\\&quot;&quot; what?</code></pre>\n<p>The generic <code>Show</code> instance for <code>[a]</code> simply intercalates <code>&quot;, &quot;</code> between\nelements.</p>\n<p>But the <code>Show</code> instance for <code>[Char]</code> behaves differently.</p>\n<p>Why is that? This is due to a language extension called\n<a href=\"https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/instances.html#overlapping-instances\" rel=\"nofollow ugc noopener\">overlapping instances</a>.</p>\n<p>It allows otherwise ambiguous instances to coexist:</p>\n<pre><code>instance Show a =&gt; Show [a] where ...\n\ninstance {-# OVERLAPPING #-} Show [Char] where ...</code></pre>\n<p>The <code>OVERLAPPING</code> pragma tells the compiler that this instance may override\nanother instance iff it is more specific.</p>\n<p>The rules for instance specificity are explained in the same link.</p>\n<p>Now, consider in Java:</p>\n<pre><code>sealed interface FwdList&lt;A&gt; extends TApp&lt;FwdList.Tag, A&gt; {\n  record Nil&lt;A&gt;() implements FwdList&lt;A&gt; {}\n\n  record Cons&lt;A&gt;(A head, FwdList&lt;A&gt; tail) implements FwdList&lt;A&gt; {}\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;FwdList&lt;A&gt;&gt; show(Show&lt;A&gt; showA) { ... }\n\n  // Ambiguous!\n  @TypeClass.Witness\n  static Show&lt;FwdList&lt;Character&gt;&gt; show() { ... }\n}</code></pre>\n<p><code>FwdList</code> (name inspired by\n<a href=\"https://en.cppreference.com/w/cpp/container/forward_list.html\" rel=\"nofollow ugc noopener\">C++&#39;s std::forward_list</a>)\nimplements a data structure like Haskell&#39;s lists.</p>\n<p>As it is, witness resolution will fail with an ambiguous witness error.</p>\n<p>In order to support overlapping instance, we must apply some changes.</p>\n<p>First, let&#39;s model Haskell&#39;s <code>OVERLAPPING</code> and <code>OVERLAPPABLE</code> pragmas:</p>\n<pre><code>@Retention(RetentionPolicy.RUNTIME)\n@interface TypeClass {\n  @Retention(RetentionPolicy.RUNTIME)\n  @interface Witness {\n    Overlap overlap() default Overlap.NONE;\n\n    enum Overlap {\n      NONE,\n      OVERLAPPING,\n      OVERLAPPABLE\n    }\n  }\n}</code></pre>\n<p>Then, we make it accessible from <code>InstanceConstructor</code>:</p>\n<pre><code>private record InstanceConstructor(FuncType func) implements WitnessRule {\n  public TypeClass.Witness.Overlap overlap() {\n    return func.java().getAnnotation(TypeClass.Witness.class).overlap();\n  }\n\n  // ...\n}</code></pre>\n<p>Finally, we implement the overlapping instances deduction algorithm as described\nin\n<a href=\"https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/instances.html#overlapping-instances\" rel=\"nofollow ugc noopener\">Haskell&#39;s spec</a>:</p>\n<pre><code>private static List&lt;InstanceConstructor&gt; reduceOverlapping(\n    List&lt;InstanceConstructor&gt; candidates) {\n  return candidates.stream()\n      .filter(\n          iX -&gt;\n              candidates.stream()\n                  .filter(iY -&gt; iX != iY)\n                  .noneMatch(iY -&gt; isOverlappedBy(iX, iY)))\n      .toList();\n}\n\nprivate static boolean isOverlappedBy(\n    InstanceConstructor iX, InstanceConstructor iY) {\n  return (iX.overlap() == OVERLAPPABLE || iY.overlap() == OVERLAPPING)\n      &amp;&amp; isSubstitutionInstance(iX, iY)\n      &amp;&amp; !isSubstitutionInstance(iY, iX);\n}\n\nprivate static boolean isSubstitutionInstance(\n    InstanceConstructor base, InstanceConstructor reference) {\n  return Unification.unify(base.func().returnType(), reference.func().returnType())\n      .fold(() -&gt; false, map -&gt; !map.isEmpty());\n}</code></pre>\n<p>And we apply it to our candidate resolution function:</p>\n<pre><code>private static List&lt;Candidate&gt; findCandidates(\n    ParsedType target, List&lt;ContextInstance&gt; context) {\n  return Stream.&lt;WitnessRule&gt;concat(\n          context.stream(),\n          reduceOverlapping(findRules(target)).stream())\n      .flatMap(...)\n      .toList();\n}</code></pre>\n<p>And, of course, we annotate our witness constructor:</p>\n<pre><code>sealed interface FwdList&lt;A&gt; extends TApp&lt;FwdList.Tag, A&gt; {\n  record Nil&lt;A&gt;() implements FwdList&lt;A&gt; {}\n\n  record Cons&lt;A&gt;(A head, FwdList&lt;A&gt; tail) implements FwdList&lt;A&gt; {}\n\n  @TypeClass.Witness\n  static &lt;A&gt; Show&lt;FwdList&lt;A&gt;&gt; show(Show&lt;A&gt; showA) { ... }\n\n  // OK now!\n  @TypeClass.Witness(overlap = OVERLAPPING)\n  static Show&lt;FwdList&lt;Character&gt;&gt; show() { ... }\n}</code></pre>\n<p>Now, our witness resolution code will pick <code>Show&lt;FwdList&lt;Character&gt;&gt;</code> because it\nis more specific AND it declares that it may overlap other instances.</p>\n<hr />\n<p><em>Originally published at <a href=\"https://garciat.com/posts/java-type-classes/\" rel=\"nofollow ugc noopener\">garciat.com</a>.</em></p>","headings":[{"level":1,"text":"Full Haskell-like Type Class resolution in Java","id":"full-haskell-like-type-class-resolution-in-java"},{"level":2,"text":"TL;DR","id":"tl-dr"},{"level":2,"text":"Intro: What are Type Classes?","id":"intro-what-are-type-classes"},{"level":2,"text":"Goal: Programmatically instantiating witnesses","id":"goal-programmatically-instantiating-witnesses"},{"level":2,"text":"Aside: Java Generics and Type Erasure","id":"aside-java-generics-and-type-erasure"},{"level":2,"text":"Aside: Capturing static types","id":"aside-capturing-static-types"},{"level":2,"text":"Aside: Understanding java.lang.reflect.Type's hierarchy","id":"aside-understanding-java-lang-reflect-type-s-hierarchy"},{"level":2,"text":"Subgoal: Parsing java.lang.reflect.Type into an AST","id":"subgoal-parsing-java-lang-reflect-type-into-an-ast"},{"level":2,"text":"Recap","id":"recap"},{"level":2,"text":"Subgoal: Witness resolution","id":"subgoal-witness-resolution"},{"level":2,"text":"Aside: Type Unification","id":"aside-type-unification"},{"level":3,"text":"Listing: Type Unification algorithm for ParsedType","id":"listing-type-unification-algorithm-for-parsedtype"},{"level":3,"text":"Extra: A type representation for static methods","id":"extra-a-type-representation-for-static-methods"},{"level":2,"text":"Subgoal: Witness resolution, part 2","id":"subgoal-witness-resolution-part-2"},{"level":2,"text":"Recap","id":"recap-2"},{"level":2,"text":"Subgoal: Witness resolution implementation","id":"subgoal-witness-resolution-implementation"},{"level":2,"text":"Examples: First-Order Type Classes & Instances","id":"examples-first-order-type-classes-instances"},{"level":3,"text":"Type Class: Show","id":"type-class-show"},{"level":3,"text":"Type Class: Monoid & Num","id":"type-class-monoid-num"},{"level":3,"text":"Type Class: PrintAll","id":"type-class-printall"},{"level":3,"text":"Type Class: Type Equality!","id":"type-class-type-equality"},{"level":2,"text":"Goal: Higher-Order Type Classes","id":"goal-higher-order-type-classes"},{"level":2,"text":"Aside: What is a kind?","id":"aside-what-is-a-kind"},{"level":2,"text":"Aside: Higher-Kinded Types in Java","id":"aside-higher-kinded-types-in-java"},{"level":2,"text":"Subgoal: Extending ParsedType to support HKTs","id":"subgoal-extending-parsedtype-to-support-hkts"},{"level":2,"text":"Aside: Kind-checking Java-embedded HKTs","id":"aside-kind-checking-java-embedded-hkts"},{"level":2,"text":"Examples: Higher-Kinded Type Clases","id":"examples-higher-kinded-type-clases"},{"level":3,"text":"Type Class: Traversable","id":"type-class-traversable"},{"level":3,"text":"Type Class: Functor, Applicative, Monad","id":"type-class-functor-applicative-monad"},{"level":2,"text":"Recap","id":"recap-3"},{"level":2,"text":"Conclusion","id":"conclusion"},{"level":3,"text":"Future work","id":"future-work"},{"level":3,"text":"Related Work","id":"related-work"},{"level":2,"text":"Annex: Context Instances","id":"annex-context-instances"},{"level":2,"text":"Annex: Overlapping Instances","id":"annex-overlapping-instances"}]}}