From 746c626cb3e9b5b661861b9744374edab73ea026 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 13:50:16 +0000 Subject: [PATCH 01/11] Initial plan From 9a4b9498f93418c1623caa8417053767afa8fda8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:04:23 +0000 Subject: [PATCH 02/11] Restructure code as library with clear API separation Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- .../com/garciat/typeclasses/Examples.java | 74 ++++++ .../java/com/garciat/typeclasses/Main.java | 241 +++++++++++++----- .../com/garciat/typeclasses/package-info.java | 56 ++++ 3 files changed, 302 insertions(+), 69 deletions(-) create mode 100644 src/main/java/com/garciat/typeclasses/Examples.java create mode 100644 src/main/java/com/garciat/typeclasses/package-info.java diff --git a/src/main/java/com/garciat/typeclasses/Examples.java b/src/main/java/com/garciat/typeclasses/Examples.java new file mode 100644 index 0000000..1e5f69b --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/Examples.java @@ -0,0 +1,74 @@ +package com.garciat.typeclasses; + +import static com.garciat.typeclasses.Functions.curry; +import static com.garciat.typeclasses.Functions.flip; +import static com.garciat.typeclasses.TyEq.refl; +import static com.garciat.typeclasses.TypeClasses.witness; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +/** Demonstration of the java-type-classes library features. */ +public final class Examples { + private Examples() {} + + public static void main(String[] args) { + System.out.println(Show.show(witness(new Ty<>() {}), new int[] {1, 2, 3, 4, 5})); + + System.out.println(Show.show(witness(new Ty<>() {}), new Integer[] {1, 2, 3, 4, 5})); + + Map>> m1 = + Map.of( + "a", + List.of(Optional.of(1), Optional.empty()), + "b", + List.of(Optional.of(2), Optional.of(3))); + + System.out.printf("show(m1) = %s\n", Show.show(witness(new Ty<>() {}), m1)); + + List> sums = List.of(new Sum<>(3), new Sum<>(5), new Sum<>(10)); + + System.out.printf( + "combineAll(%s) = %s\n", sums, Monoid.combineAll(witness(new Ty<>() {}), sums)); + + System.out.printf("eq(m1, m1) = %s\n", Eq.eq(witness(new Ty<>() {}), m1, m1)); + + Optional m5 = Optional.of(5); + Optional m10 = Optional.of(10); + + System.out.printf( + "compare(%s, %s) = %s\n", m5, m10, Ord.compare(witness(new Ty<>() {}), m5, m10)); + + Arbitrary, List>>> arbFunc = + witness(new Ty<>() {}); + var f = arbFunc.arbitrary().generate(42L, 10); + + System.out.println("f(10) = " + f.apply(Optional.of(5))); + + System.out.println( + Traversable.traverse( + witness(new Ty<>() {}), witness(new Ty<>() {}), JavaList.of(1, 2, 3), Maybe::just)); + + System.out.println(Show.show(witness(new Ty<>() {}), FwdList.of('h', 'e', 'l', 'l', 'o'))); + + example(witness(new Ty<>() {}), 123); + + F3 sum = SumAllInt.of(witness(new Ty<>() {})); + System.out.println(sum.apply(1, 2, 3)); + + F3, Integer, Void> printer = PrintAll.of(witness(new Ty<>() {})); + printer.apply("Items:", JavaList.of("apple", "banana", "cherry"), 0); + + Foldable foldableFwdList = witness(new Ty<>() {}); + + System.out.println(foldableFwdList.length(FwdList.of(1, 2, 3, 4, 5))); + + System.out.println(foldableFwdList.toList(FwdList.of(1, 2, 3))); + } + + static void example(Show showA, A value) { + System.out.println(Show.show(witness(new Ty<>() {}, new Ctx<>(showA) {}), JavaList.of(value))); + } +} diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 706fc5f..e50106e 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -37,89 +37,79 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class Main { - public static void main(String[] args) { - System.out.println(Show.show(witness(new Ty<>() {}), new int[] {1, 2, 3, 4, 5})); - - System.out.println(Show.show(witness(new Ty<>() {}), new Integer[] {1, 2, 3, 4, 5})); - - Map>> m1 = - Map.of( - "a", - List.of(Optional.of(1), Optional.empty()), - "b", - List.of(Optional.of(2), Optional.of(3))); - - System.out.printf("show(m1) = %s\n", Show.show(witness(new Ty<>() {}), m1)); - - List> sums = List.of(new Sum<>(3), new Sum<>(5), new Sum<>(10)); - - System.out.printf( - "combineAll(%s) = %s\n", sums, Monoid.combineAll(witness(new Ty<>() {}), sums)); - - System.out.printf("eq(m1, m1) = %s\n", Eq.eq(witness(new Ty<>() {}), m1, m1)); - - Optional m5 = Optional.of(5); - Optional m10 = Optional.of(10); - - System.out.printf( - "compare(%s, %s) = %s\n", m5, m10, Ord.compare(witness(new Ty<>() {}), m5, m10)); - - Arbitrary, List>>> arbFunc = - witness(new Ty<>() {}); - var f = arbFunc.arbitrary().generate(42L, 10); - - System.out.println("f(10) = " + f.apply(Optional.of(5))); - - System.out.println( - Traversable.traverse( - witness(new Ty<>() {}), witness(new Ty<>() {}), JavaList.of(1, 2, 3), Maybe::just)); - - System.out.println(Show.show(witness(new Ty<>() {}), FwdList.of('h', 'e', 'l', 'l', 'o'))); - - example(witness(new Ty<>() {}), 123); - - F3 sum = SumAllInt.of(witness(new Ty<>() {})); - System.out.println(sum.apply(1, 2, 3)); - - F3, Integer, Void> printer = PrintAll.of(witness(new Ty<>() {})); - printer.apply("Items:", JavaList.of("apple", "banana", "cherry"), 0); - - Foldable foldableFwdList = witness(new Ty<>() {}); - - System.out.println(foldableFwdList.length(FwdList.of(1, 2, 3, 4, 5))); - - System.out.println(foldableFwdList.toList(FwdList.of(1, 2, 3))); - } - - static void example(Show showA, A value) { - System.out.println(Show.show(witness(new Ty<>() {}, new Ctx<>(showA) {}), JavaList.of(value))); - } +/** + * Core type class infrastructure for Java. + * + *

This class has been retained for backward compatibility but is no longer the main entry point. + * The library should be accessed through the public type classes and data types. + * + * @deprecated Use {@link Examples} for demonstration code, or the individual type classes directly. + */ +@Deprecated +public final class Main { + private Main() {} } // ==== Type System ==== -// This is how we get basic kind checking in Java +/** + * Base interface for kind-level types, providing basic kind checking in Java. + * + *

This interface is used to represent type-level kinds, similar to kinds in Haskell's type + * system. + * + *

PUBLIC API: This is part of the library's public API. Users implementing custom data + * types will need to use this interface. + */ interface Kind { + /** Base interface for all kinds. */ sealed interface Base {} - // KStar = * + /** KStar represents the kind * (star) - the kind of proper types. */ final class KStar implements Base {} - // KArr k = * -> k + /** KArr k represents the kind * -> k - the kind of type constructors. */ final class KArr implements Base {} } +/** + * Base class for type-level tags. Subclasses of this class represent type constructor tags used in + * higher-kinded type encoding. + * + *

PUBLIC API: This is part of the library's public API. Users implementing custom data + * types will need to extend this class. + * + * @param the kind of this tag + */ abstract class TagBase implements Kind {} -// Full application of a unary type constructor -// TApp :: (* -> *) -> * -> * +/** + * Full application of a unary type constructor. + * + *

TApp :: (* -> *) -> * -> * + * + *

PUBLIC API: This is part of the library's public API. Users will use this in type + * signatures. + * + * @param the type constructor tag + * @param the applied type argument + */ interface TApp>, A> extends Kind {} -// Partial application of a binary type constructor -// TPar :: (* -> * -> *) -> * -> (* -> *) +/** + * Partial application of a binary type constructor. + * + *

TPar :: (* -> * -> *) -> * -> (* -> *) + * + *

PUBLIC API: This is part of the library's public API. Users will use this in type + * signatures. + * + * @param the type constructor tag + * @param the first applied type argument + */ interface TPar>>, A> extends Kind> {} +// Internal type parsing sealed interface ParsedType { record Var(TypeVariable java) implements ParsedType {} @@ -188,6 +178,7 @@ private static Maybe> parseAppType(ParameterizedType t) { } } +// Internal unification algorithm class Unification { public static Maybe> unify(ParsedType t1, ParsedType t2) { return switch (Pair.of(t1, t2)) { @@ -231,6 +222,7 @@ public static List substituteAll( } } +// Internal function type representation record FuncType(Method java, List paramTypes, ParsedType returnType) { public String format() { return String.format( @@ -257,45 +249,138 @@ public static FuncType parse(Method method) { // === Type Class System === +/** + * Marks an interface as a type class. + * + *

Type classes are interfaces that define a set of operations that can be implemented for + * various types. The type class system uses compile-time and runtime reflection to automatically + * resolve instances. + * + *

PUBLIC API: This is part of the library's public API. Users define and implement type + * classes using this annotation. + */ @Retention(RetentionPolicy.RUNTIME) @interface TypeClass { + /** Marks a method as a witness (instance) of a type class. */ @Retention(RetentionPolicy.RUNTIME) @interface Witness { + /** + * Specifies the overlap behavior for this witness. + * + * @return the overlap behavior + */ Overlap overlap() default Overlap.NONE; + /** Defines how instances can overlap with other instances. */ enum Overlap { + /** No overlap allowed (default). */ NONE, + /** This instance can overlap and take precedence over others. */ OVERLAPPING, + /** This instance can be overlapped by others. */ OVERLAPPABLE } } } +/** + * Type token for capturing type information at runtime. + * + *

Usage: + * + *

{@code
+ * Show showString = TypeClasses.witness(new Ty>() {});
+ * }
+ * + *

PUBLIC API: This is the main interface users interact with to summon type class + * instances. + * + * @param the type being captured + */ interface Ty { + /** + * Returns the captured type. + * + * @return the Type object representing T + */ default Type type() { return requireNonNull( ((ParameterizedType) getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]); } } +/** + * Context token for capturing type class instances at runtime. + * + *

Usage: + * + *

{@code
+ * Show showString = ...;
+ * Ctx> ctx = new Ctx<>(showString) {};
+ * }
+ * + *

PUBLIC API: Used for passing explicit type class instances to witness resolution. + * + * @param the type class instance type + */ abstract class Ctx { private final T instance; - Ctx(T instance) { + /** + * Constructs a context with the given instance. + * + * @param instance the type class instance + */ + protected Ctx(T instance) { this.instance = instance; } + /** + * Returns the instance. + * + * @return the type class instance + */ public T instance() { return instance; } + /** + * Returns the captured type. + * + * @return the Type object representing T + */ public Type type() { return requireNonNull( ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } } +// Internal witness resolution utilities +/** + * Central facility for type class witness resolution. + * + *

PUBLIC API: The {@link #witness} method is the main entry point for the library. + */ class TypeClasses { + /** + * Resolves and returns a witness (instance) of a type class for the given type. + * + *

This is the main entry point for using the type class system. It automatically finds and + * instantiates the appropriate type class instance based on the provided type token. + * + *

Example: + * + *

{@code
+   * Show> showListInt = TypeClasses.witness(new Ty>>() {});
+   * String result = showListInt.show(List.of(1, 2, 3));
+   * }
+ * + * @param the type class instance type + * @param ty the type token capturing the desired type class instance + * @param context optional context instances to use in resolution + * @return the resolved type class instance + * @throws WitnessResolutionException if no suitable instance can be found + */ public static T witness(Ty ty, Ctx... context) { return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) { case Either.Left(SummonError error) -> @@ -314,7 +399,8 @@ private static List parseContext(Ctx[] context) { .toList(); } - public static class WitnessResolutionException extends RuntimeException { + /** Exception thrown when a type class witness cannot be resolved. PUBLIC API. */ + static class WitnessResolutionException extends RuntimeException { private WitnessResolutionException(SummonError error) { super(error.format()); } @@ -1726,8 +1812,15 @@ static
Parser unwrap(TApp value) { } } -// === Weird Type Class Examples === +// === Example Type Class Implementations === +// These are example implementations demonstrating advanced type class features. +// They are package-private as they're primarily for demonstration purposes. +/** + * Example type class demonstrating variadic functions through type class resolution. + * + * @param the result type + */ @TypeClass interface SumAllInt { A sum(List list); @@ -1764,7 +1857,10 @@ static SumAllInt> func3( } /** - * @implNote Source + * Example type class for variadic printing. + * + * @param the result type + * @see Source */ @TypeClass interface PrintAll { @@ -1806,6 +1902,7 @@ static PrintAll> func3( } } +/** Helper interface for one-argument functions used in examples. */ @FunctionalInterface interface F1 { R apply(A a); @@ -1815,6 +1912,7 @@ static F1 of(Function f) { } } +/** Helper interface for two-argument functions used in examples. */ @FunctionalInterface interface F2 { R apply(A a, B b); @@ -1824,6 +1922,7 @@ static F2 of(Function> f) { } } +/** Helper interface for three-argument functions used in examples. */ @FunctionalInterface interface F3 { R apply(A a, B b, C c); @@ -1835,6 +1934,7 @@ static F3 of(Function>> f // === Utilities === +// Internal result type for instance selection sealed interface ZeroOneMore { record Zero() implements ZeroOneMore {} @@ -1851,6 +1951,7 @@ static ZeroOneMore of(List list) { } } +// Internal list utilities class Lists { public static List map(List list, Function f) { return list.stream().map(f).collect(Collectors.toList()); @@ -1862,6 +1963,7 @@ public static List concat(List... lists) { } } +// Internal map utilities class Maps { public static Map merge(Map m1, Map m2) { Map result = new HashMap<>(m1); @@ -1875,6 +1977,7 @@ public static Map merge(Map m1, Map m2) { } } +// Internal function utilities class Functions { public static BiFunction flip(BiFunction f) { return (b, a) -> f.apply(a, b); diff --git a/src/main/java/com/garciat/typeclasses/package-info.java b/src/main/java/com/garciat/typeclasses/package-info.java new file mode 100644 index 0000000..15d1a36 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/package-info.java @@ -0,0 +1,56 @@ +/** + * Java Type Classes Library + * + *

This library provides a type class system for Java, inspired by Haskell's type classes. It + * allows you to define type classes (interfaces with @TypeClass annotation) and automatically + * resolve instances for various types. + * + *

Public API

+ * + *

The main entry point is {@link com.garciat.typeclasses.TypeClasses#witness}, which resolves + * type class instances: + * + *

{@code
+ * Show> showListInt = TypeClasses.witness(new Ty>>() {});
+ * String result = showListInt.show(List.of(1, 2, 3));
+ * }
+ * + *

Core Type Classes

+ * + *
    + *
  • {@link com.garciat.typeclasses.Show} - Convert values to strings + *
  • {@link com.garciat.typeclasses.Eq} - Equality testing + *
  • {@link com.garciat.typeclasses.Ord} - Ordering comparisons + *
  • {@link com.garciat.typeclasses.Monoid} - Associative binary operations with identity + *
  • {@link com.garciat.typeclasses.Functor} - Mappable type constructors + *
  • {@link com.garciat.typeclasses.Applicative} - Application of functions in a context + *
  • {@link com.garciat.typeclasses.Monad} - Sequential composition of computations + *
  • {@link com.garciat.typeclasses.Foldable} - Structures that can be folded + *
  • {@link com.garciat.typeclasses.Traversable} - Structures that can be traversed + *
+ * + *

Data Types

+ * + *
    + *
  • {@link com.garciat.typeclasses.Maybe} - Optional values + *
  • {@link com.garciat.typeclasses.Either} - Sum types (Left or Right) + *
  • {@link com.garciat.typeclasses.JavaList} - List with type class instances + *
  • {@link com.garciat.typeclasses.FwdList} - Functional forward list + *
  • {@link com.garciat.typeclasses.Parser} - Parser combinators + *
  • {@link com.garciat.typeclasses.State} - State monad + *
+ * + *

Type System Infrastructure

+ * + *
    + *
  • {@link com.garciat.typeclasses.Kind} - Kind system for higher-kinded types + *
  • {@link com.garciat.typeclasses.TApp} - Type application + *
  • {@link com.garciat.typeclasses.TPar} - Partial type application + *
  • {@link com.garciat.typeclasses.TagBase} - Base class for type tags + *
+ * + *

Examples

+ * + *

See {@link com.garciat.typeclasses.Examples} for usage examples. + */ +package com.garciat.typeclasses; From a9425cc6b5675b9650282cc4c15ac23fbd992f83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:06:05 +0000 Subject: [PATCH 03/11] Add comprehensive README and apply code formatting Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- README.md | 97 ++++++++++++++++++- .../com/garciat/typeclasses/Examples.java | 3 - .../java/com/garciat/typeclasses/Main.java | 1 - 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e5ec91f..e055d37 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,96 @@ -Conventions: +# Java Type Classes -- Use google-java-format +A type class system for Java, inspired by Haskell's type classes. + +## Overview + +This library provides a way to define and use type classes in Java, enabling ad-hoc polymorphism through automatic instance resolution. It includes a rich set of predefined type classes and data types with higher-kinded type support. + +## Usage + +### Basic Example + +```java +import static com.garciat.typeclasses.TypeClasses.witness; + +// Automatically resolve a Show instance for List +Show> showListInt = witness(new Ty<>() {}); +String result = showListInt.show(List.of(1, 2, 3)); +// result: "[1, 2, 3]" +``` + +### Core Type Classes + +The library provides several built-in type classes: + +- **Show** - Convert values to strings +- **Eq** - Equality testing +- **Ord** - Ordering comparisons +- **Monoid** - Associative binary operations with identity +- **Functor** - Mappable type constructors +- **Applicative** - Application of functions in a context +- **Monad** - Sequential composition of computations +- **Foldable** - Structures that can be folded +- **Traversable** - Structures that can be traversed + +### Data Types + +The library includes functional data types with type class instances: + +- **Maybe** - Optional values (`Just` or `Nothing`) +- **Either** - Sum types (`Left` or `Right`) +- **JavaList** - List with type class instances +- **FwdList** - Functional forward list +- **Parser** - Parser combinators +- **State** - State monad + +### Higher-Kinded Types + +The library supports higher-kinded types through a defunctionalization encoding: + +```java +// Work with Functors abstractly +Functor functorMaybe = witness(new Ty<>() {}); +TApp maybeInt = Maybe.just(42); +TApp maybeStr = functorMaybe.map(Object::toString, maybeInt); +``` + +## Examples + +See the `Examples` class for comprehensive usage examples: + +```bash +mvn compile exec:java -Dexec.mainClass="com.garciat.typeclasses.Examples" +``` + +## API Structure + +### Public API + +The main entry point is `TypeClasses.witness()` which resolves type class instances. All type classes (marked with `@TypeClass`) and data types are part of the public API. + +Key public components: +- `TypeClasses.witness()` - Resolve type class instances +- `Ty` - Type token for capturing types +- `Ctx` - Context token for explicit instances +- `@TypeClass` - Annotation for defining type classes +- `Kind`, `TApp`, `TPar`, `TagBase` - Higher-kinded type infrastructure + +### Internal Implementation + +Internal implementation details (parsing, unification, witness resolution algorithms) are package-private and should not be relied upon by library users. + +## Building + +```bash +mvn clean compile test +``` + +## Conventions + +- Use google-java-format for code formatting +- Java 21 is required + +## License + +See repository for license information. diff --git a/src/main/java/com/garciat/typeclasses/Examples.java b/src/main/java/com/garciat/typeclasses/Examples.java index 1e5f69b..2faadc8 100644 --- a/src/main/java/com/garciat/typeclasses/Examples.java +++ b/src/main/java/com/garciat/typeclasses/Examples.java @@ -1,8 +1,5 @@ package com.garciat.typeclasses; -import static com.garciat.typeclasses.Functions.curry; -import static com.garciat.typeclasses.Functions.flip; -import static com.garciat.typeclasses.TyEq.refl; import static com.garciat.typeclasses.TypeClasses.witness; import java.util.List; diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index e50106e..5679b0e 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -5,7 +5,6 @@ import static com.garciat.typeclasses.TyEq.refl; import static com.garciat.typeclasses.TypeClass.Witness.Overlap.OVERLAPPABLE; import static com.garciat.typeclasses.TypeClass.Witness.Overlap.OVERLAPPING; -import static com.garciat.typeclasses.TypeClasses.witness; import static java.lang.reflect.AccessFlag.PUBLIC; import static java.lang.reflect.AccessFlag.STATIC; import static java.util.Objects.requireNonNull; From 5b806b1f47863c18148dc3da764dd12e543be667 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:09:09 +0000 Subject: [PATCH 04/11] Add comprehensive library usage tests Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- .../garciat/typeclasses/LibraryUsageTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/test/java/com/garciat/typeclasses/LibraryUsageTest.java diff --git a/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java b/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java new file mode 100644 index 0000000..00de6fd --- /dev/null +++ b/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java @@ -0,0 +1,103 @@ +package com.garciat.typeclasses; + +import static com.garciat.typeclasses.TypeClasses.witness; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** Tests demonstrating the library's public API usage. */ +final class LibraryUsageTest { + + @Test + void canResolveShowForBasicTypes() { + Show showInt = witness(new Ty<>() {}); + assertEquals("42", showInt.show(42)); + + Show showString = witness(new Ty<>() {}); + assertEquals("\"hello\"", showString.show("hello")); + } + + @Test + void canResolveShowForComplexTypes() { + Show> showListInt = witness(new Ty<>() {}); + String result = showListInt.show(List.of(1, 2, 3)); + assertEquals("[1, 2, 3]", result); + } + + @Test + void canResolveShowForNestedTypes() { + Show>> showOptListStr = witness(new Ty<>() {}); + String result = showOptListStr.show(Optional.of(List.of("a", "b"))); + assertEquals("Some([\"a\", \"b\"])", result); + } + + @Test + void canResolveEqForBasicTypes() { + Eq eqInt = witness(new Ty<>() {}); + assertTrue(eqInt.eq(42, 42)); + assertFalse(eqInt.eq(42, 43)); + } + + @Test + void canResolveEqForComplexTypes() { + Eq> eqListStr = witness(new Ty<>() {}); + assertTrue(eqListStr.eq(List.of("a", "b"), List.of("a", "b"))); + assertFalse(eqListStr.eq(List.of("a", "b"), List.of("a", "c"))); + } + + @Test + void canResolveOrdForBasicTypes() { + Ord ordInt = witness(new Ty<>() {}); + assertEquals(Ordering.LT, ordInt.compare(1, 2)); + assertEquals(Ordering.EQ, ordInt.compare(2, 2)); + assertEquals(Ordering.GT, ordInt.compare(3, 2)); + } + + @Test + void canResolveMonoidForString() { + Monoid monoidString = witness(new Ty<>() {}); + assertEquals("", monoidString.identity()); + assertEquals("ab", monoidString.combine("a", "b")); + } + + @Test + void canUseMonoidCombineAll() { + List strings = List.of("Hello", " ", "World"); + String result = Monoid.combineAll(witness(new Ty<>() {}), strings); + assertEquals("Hello World", result); + } + + @Test + void canUseFunctorMap() { + Functor functorMaybe = witness(new Ty<>() {}); + TApp maybeInt = Maybe.just(42); + TApp maybeStr = functorMaybe.map(Object::toString, maybeInt); + assertEquals("42", Maybe.unwrap(maybeStr).fold(() -> null, x -> x)); + } + + @Test + void canUseApplicativePure() { + Applicative appMaybe = witness(new Ty<>() {}); + TApp maybeInt = appMaybe.pure(42); + Integer unwrapped = Maybe.unwrap(maybeInt).fold(() -> null, x -> x); + assertEquals(42, unwrapped.intValue()); + } + + @Test + void canUseMonadFlatMap() { + Monad monadMaybe = witness(new Ty<>() {}); + TApp maybeInt = Maybe.just(42); + TApp result = monadMaybe.flatMap(x -> Maybe.just(x * 2), maybeInt); + Integer unwrapped = Maybe.unwrap(result).fold(() -> null, x -> x); + assertEquals(84, unwrapped.intValue()); + } + + @Test + void canUseFoldableLength() { + Foldable foldableFwdList = witness(new Ty<>() {}); + FwdList list = FwdList.of(1, 2, 3, 4, 5); + assertEquals(5, (int) foldableFwdList.length(list)); + } +} From 888b5bcae5f72d6ee009d6ed3d5b09790e668931 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:11:10 +0000 Subject: [PATCH 05/11] Address code review: make WitnessResolutionException and Ctx constructor public Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- src/main/java/com/garciat/typeclasses/Main.java | 4 ++-- src/main/java/com/garciat/typeclasses/package-info.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 5679b0e..3ea5124 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -330,7 +330,7 @@ abstract class Ctx { * * @param instance the type class instance */ - protected Ctx(T instance) { + public Ctx(T instance) { this.instance = instance; } @@ -399,7 +399,7 @@ private static List parseContext(Ctx[] context) { } /** Exception thrown when a type class witness cannot be resolved. PUBLIC API. */ - static class WitnessResolutionException extends RuntimeException { + public static class WitnessResolutionException extends RuntimeException { private WitnessResolutionException(SummonError error) { super(error.format()); } diff --git a/src/main/java/com/garciat/typeclasses/package-info.java b/src/main/java/com/garciat/typeclasses/package-info.java index 15d1a36..0cad5ba 100644 --- a/src/main/java/com/garciat/typeclasses/package-info.java +++ b/src/main/java/com/garciat/typeclasses/package-info.java @@ -11,7 +11,7 @@ * type class instances: * *

{@code
- * Show> showListInt = TypeClasses.witness(new Ty>>() {});
+ * Show> showListInt = TypeClasses.witness(new Ty<>() {});
  * String result = showListInt.show(List.of(1, 2, 3));
  * }
* From f3e94ed9831b65463c661437d11e2e65226df38c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:33:21 +0000 Subject: [PATCH 06/11] Reorganize code into api, impl, classes, and types packages with module exports Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- .../com/garciat/typeclasses/Examples.java | 2 + .../java/com/garciat/typeclasses/Main.java | 452 +----------------- .../com/garciat/typeclasses/TypeClasses.java | 225 +++++++++ .../java/com/garciat/typeclasses/api/Ctx.java | 51 ++ .../com/garciat/typeclasses/api/Kind.java | 21 + .../com/garciat/typeclasses/api/TApp.java | 14 + .../com/garciat/typeclasses/api/TPar.java | 15 + .../com/garciat/typeclasses/api/TagBase.java | 12 + .../java/com/garciat/typeclasses/api/Ty.java | 31 ++ .../garciat/typeclasses/api/TypeClass.java | 38 ++ .../typeclasses/classes/package-info.java | 21 + .../garciat/typeclasses/impl/Functions.java | 15 + .../com/garciat/typeclasses/impl/Lists.java | 18 + .../com/garciat/typeclasses/impl/Maps.java | 18 + .../garciat/typeclasses/impl/ZeroOneMore.java | 20 + .../typeclasses/types/package-info.java | 19 + src/main/java/module-info.java | 28 ++ .../garciat/typeclasses/LibraryUsageTest.java | 2 + 18 files changed, 566 insertions(+), 436 deletions(-) create mode 100644 src/main/java/com/garciat/typeclasses/TypeClasses.java create mode 100644 src/main/java/com/garciat/typeclasses/api/Ctx.java create mode 100644 src/main/java/com/garciat/typeclasses/api/Kind.java create mode 100644 src/main/java/com/garciat/typeclasses/api/TApp.java create mode 100644 src/main/java/com/garciat/typeclasses/api/TPar.java create mode 100644 src/main/java/com/garciat/typeclasses/api/TagBase.java create mode 100644 src/main/java/com/garciat/typeclasses/api/Ty.java create mode 100644 src/main/java/com/garciat/typeclasses/api/TypeClass.java create mode 100644 src/main/java/com/garciat/typeclasses/classes/package-info.java create mode 100644 src/main/java/com/garciat/typeclasses/impl/Functions.java create mode 100644 src/main/java/com/garciat/typeclasses/impl/Lists.java create mode 100644 src/main/java/com/garciat/typeclasses/impl/Maps.java create mode 100644 src/main/java/com/garciat/typeclasses/impl/ZeroOneMore.java create mode 100644 src/main/java/com/garciat/typeclasses/types/package-info.java create mode 100644 src/main/java/module-info.java diff --git a/src/main/java/com/garciat/typeclasses/Examples.java b/src/main/java/com/garciat/typeclasses/Examples.java index 2faadc8..e78d8b1 100644 --- a/src/main/java/com/garciat/typeclasses/Examples.java +++ b/src/main/java/com/garciat/typeclasses/Examples.java @@ -2,6 +2,8 @@ import static com.garciat.typeclasses.TypeClasses.witness; +import com.garciat.typeclasses.api.Ctx; +import com.garciat.typeclasses.api.Ty; import java.util.List; import java.util.Map; import java.util.Optional; diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 3ea5124..ceba9e7 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -1,19 +1,20 @@ package com.garciat.typeclasses; -import static com.garciat.typeclasses.Functions.curry; -import static com.garciat.typeclasses.Functions.flip; import static com.garciat.typeclasses.TyEq.refl; -import static com.garciat.typeclasses.TypeClass.Witness.Overlap.OVERLAPPABLE; -import static com.garciat.typeclasses.TypeClass.Witness.Overlap.OVERLAPPING; -import static java.lang.reflect.AccessFlag.PUBLIC; -import static java.lang.reflect.AccessFlag.STATIC; -import static java.util.Objects.requireNonNull; +import static com.garciat.typeclasses.api.TypeClass.Witness.Overlap.OVERLAPPING; +import static com.garciat.typeclasses.impl.Functions.curry; +import static com.garciat.typeclasses.impl.Functions.flip; import static java.util.function.Function.identity; -import com.garciat.typeclasses.Kind.KArr; -import com.garciat.typeclasses.Kind.KStar; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import com.garciat.typeclasses.api.Kind; +import com.garciat.typeclasses.api.Kind.KArr; +import com.garciat.typeclasses.api.Kind.KStar; +import com.garciat.typeclasses.api.TApp; +import com.garciat.typeclasses.api.TPar; +import com.garciat.typeclasses.api.TagBase; +import com.garciat.typeclasses.api.TypeClass; +import com.garciat.typeclasses.impl.Lists; +import com.garciat.typeclasses.impl.Maps; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -23,7 +24,6 @@ import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -50,63 +50,7 @@ private Main() {} } // ==== Type System ==== - -/** - * Base interface for kind-level types, providing basic kind checking in Java. - * - *

This interface is used to represent type-level kinds, similar to kinds in Haskell's type - * system. - * - *

PUBLIC API: This is part of the library's public API. Users implementing custom data - * types will need to use this interface. - */ -interface Kind { - /** Base interface for all kinds. */ - sealed interface Base {} - - /** KStar represents the kind * (star) - the kind of proper types. */ - final class KStar implements Base {} - - /** KArr k represents the kind * -> k - the kind of type constructors. */ - final class KArr implements Base {} -} - -/** - * Base class for type-level tags. Subclasses of this class represent type constructor tags used in - * higher-kinded type encoding. - * - *

PUBLIC API: This is part of the library's public API. Users implementing custom data - * types will need to extend this class. - * - * @param the kind of this tag - */ -abstract class TagBase implements Kind {} - -/** - * Full application of a unary type constructor. - * - *

TApp :: (* -> *) -> * -> * - * - *

PUBLIC API: This is part of the library's public API. Users will use this in type - * signatures. - * - * @param the type constructor tag - * @param the applied type argument - */ -interface TApp>, A> extends Kind {} - -/** - * Partial application of a binary type constructor. - * - *

TPar :: (* -> * -> *) -> * -> (* -> *) - * - *

PUBLIC API: This is part of the library's public API. Users will use this in type - * signatures. - * - * @param the type constructor tag - * @param the first applied type argument - */ -interface TPar>>, A> extends Kind> {} +// Kind, TApp, TPar, TagBase are now in com.garciat.typeclasses.api package // Internal type parsing sealed interface ParsedType { @@ -247,321 +191,10 @@ public static FuncType parse(Method method) { } // === Type Class System === - -/** - * Marks an interface as a type class. - * - *

Type classes are interfaces that define a set of operations that can be implemented for - * various types. The type class system uses compile-time and runtime reflection to automatically - * resolve instances. - * - *

PUBLIC API: This is part of the library's public API. Users define and implement type - * classes using this annotation. - */ -@Retention(RetentionPolicy.RUNTIME) -@interface TypeClass { - /** Marks a method as a witness (instance) of a type class. */ - @Retention(RetentionPolicy.RUNTIME) - @interface Witness { - /** - * Specifies the overlap behavior for this witness. - * - * @return the overlap behavior - */ - Overlap overlap() default Overlap.NONE; - - /** Defines how instances can overlap with other instances. */ - enum Overlap { - /** No overlap allowed (default). */ - NONE, - /** This instance can overlap and take precedence over others. */ - OVERLAPPING, - /** This instance can be overlapped by others. */ - OVERLAPPABLE - } - } -} - -/** - * Type token for capturing type information at runtime. - * - *

Usage: - * - *

{@code
- * Show showString = TypeClasses.witness(new Ty>() {});
- * }
- * - *

PUBLIC API: This is the main interface users interact with to summon type class - * instances. - * - * @param the type being captured - */ -interface Ty { - /** - * Returns the captured type. - * - * @return the Type object representing T - */ - default Type type() { - return requireNonNull( - ((ParameterizedType) getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]); - } -} - -/** - * Context token for capturing type class instances at runtime. - * - *

Usage: - * - *

{@code
- * Show showString = ...;
- * Ctx> ctx = new Ctx<>(showString) {};
- * }
- * - *

PUBLIC API: Used for passing explicit type class instances to witness resolution. - * - * @param the type class instance type - */ -abstract class Ctx { - private final T instance; - - /** - * Constructs a context with the given instance. - * - * @param instance the type class instance - */ - public Ctx(T instance) { - this.instance = instance; - } - - /** - * Returns the instance. - * - * @return the type class instance - */ - public T instance() { - return instance; - } - - /** - * Returns the captured type. - * - * @return the Type object representing T - */ - public Type type() { - return requireNonNull( - ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); - } -} +// TypeClass, Ty, Ctx are now in com.garciat.typeclasses.api package // Internal witness resolution utilities -/** - * Central facility for type class witness resolution. - * - *

PUBLIC API: The {@link #witness} method is the main entry point for the library. - */ -class TypeClasses { - /** - * Resolves and returns a witness (instance) of a type class for the given type. - * - *

This is the main entry point for using the type class system. It automatically finds and - * instantiates the appropriate type class instance based on the provided type token. - * - *

Example: - * - *

{@code
-   * Show> showListInt = TypeClasses.witness(new Ty>>() {});
-   * String result = showListInt.show(List.of(1, 2, 3));
-   * }
- * - * @param the type class instance type - * @param ty the type token capturing the desired type class instance - * @param context optional context instances to use in resolution - * @return the resolved type class instance - * @throws WitnessResolutionException if no suitable instance can be found - */ - public static T witness(Ty ty, Ctx... context) { - return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) { - case Either.Left(SummonError error) -> - throw new WitnessResolutionException(error); - case Either.Right(Object instance) -> { - @SuppressWarnings("unchecked") - T typedInstance = (T) instance; - yield typedInstance; - } - }; - } - - private static List parseContext(Ctx[] context) { - return Arrays.stream(context) - .map(ctx -> new ContextInstance(ctx.instance(), ParsedType.parse(ctx.type()))) - .toList(); - } - - /** Exception thrown when a type class witness cannot be resolved. PUBLIC API. */ - public static class WitnessResolutionException extends RuntimeException { - private WitnessResolutionException(SummonError error) { - super(error.format()); - } - } - - private sealed interface SummonError { - record NotFound(ParsedType target) implements SummonError {} - - record Ambiguous(ParsedType target, List candidates) implements SummonError {} - - record Nested(ParsedType target, SummonError cause) implements SummonError {} - - default String format() { - return switch (this) { - case NotFound(ParsedType target) -> "No witness found for type: " + target.format(); - case Ambiguous(ParsedType target, List candidates) -> - "Ambiguous witnesses found for type: " - + target.format() - + "\nCandidates:\n" - + candidates.stream() - .map(c -> c.rule().toString()) - .collect(Collectors.joining("\n")) - .indent(2); - case Nested(ParsedType target, SummonError cause) -> - "While summoning witness for type: " - + target.format() - + "\nCaused by: " - + cause.format().indent(2); - }; - } - } - - private static Either summon( - ParsedType target, List context) { - return switch (ZeroOneMore.of(findCandidates(target, context))) { - case ZeroOneMore.One(Candidate(var rule, var requirements)) -> - summonAll(requirements, context) - .map(rule::instantiate) - .mapLeft(error -> new SummonError.Nested(target, error)); - case ZeroOneMore.Zero() -> Either.left(new SummonError.NotFound(target)); - case ZeroOneMore.More(var candidates) -> - Either.left(new SummonError.Ambiguous(target, candidates)); - }; - } - - private static Either> summonAll( - List targets, List context) { - return Either.traverse(targets, target -> summon(target, context)); - } - - private record Candidate(WitnessRule rule, List requirements) {} - - private static List findCandidates(ParsedType target, List context) { - return Stream.concat( - context.stream(), reduceOverlapping(findRules(target)).stream()) - .flatMap( - rule -> - rule - .tryMatch(target) - .map(requirements -> new Candidate(rule, requirements)) - .stream()) - .toList(); - } - - /** - * @implSpec
6.8.8.5. - * Overlapping instances - */ - private static List reduceOverlapping(List candidates) { - return candidates.stream() - .filter( - iX -> - candidates.stream().filter(iY -> iX != iY).noneMatch(iY -> isOverlappedBy(iX, iY))) - .toList(); - } - - private static boolean isOverlappedBy(InstanceConstructor iX, InstanceConstructor iY) { - return (iX.overlap() == OVERLAPPABLE || iY.overlap() == OVERLAPPING) - && isSubstitutionInstance(iX, iY) - && !isSubstitutionInstance(iY, iX); - } - - private static boolean isSubstitutionInstance( - InstanceConstructor base, InstanceConstructor reference) { - return Unification.unify(base.func().returnType(), reference.func().returnType()) - .fold(() -> false, map -> !map.isEmpty()); - } - - private static List findRules(ParsedType target) { - return switch (target) { - case ParsedType.App(var fun, var arg) -> Lists.concat(findRules(fun), findRules(arg)); - case ParsedType.Const(var java) -> rulesOf(java); - case ParsedType.Var(var java) -> List.of(); - case ParsedType.ArrayOf(var elem) -> List.of(); - case ParsedType.Primitive(var java) -> List.of(); - }; - } - - private static List rulesOf(Class cls) { - return Arrays.stream(cls.getDeclaredMethods()) - .filter(TypeClasses::isWitnessMethod) - .map(FuncType::parse) - .map(InstanceConstructor::new) - .toList(); - } - - private static boolean isWitnessMethod(Method m) { - return m.accessFlags().contains(PUBLIC) - && m.accessFlags().contains(STATIC) - && m.isAnnotationPresent(TypeClass.Witness.class); - } - - private sealed interface WitnessRule { - Maybe> tryMatch(ParsedType target); - - Object instantiate(List dependencies); - } - - private record ContextInstance(Object instance, ParsedType type) implements WitnessRule { - @Override - public Maybe> tryMatch(ParsedType target) { - return target.equals(type) ? Maybe.just(List.of()) : Maybe.nothing(); - } - - @Override - public Object instantiate(List dependencies) { - return instance; - } - - @Override - public String toString() { - return "context instance: " + type.format(); - } - } - - private record InstanceConstructor(FuncType func) implements WitnessRule { - public TypeClass.Witness.Overlap overlap() { - return func.java().getAnnotation(TypeClass.Witness.class).overlap(); - } - - @Override - public Maybe> tryMatch(ParsedType target) { - return Unification.unify(func.returnType(), target) - .map(map -> Unification.substituteAll(map, func.paramTypes())); - } - - @Override - public Object instantiate(List dependencies) { - try { - return func.java().invoke(null, dependencies.toArray()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return func.format(); - } - } -} +// TypeClasses is now in com.garciat.typeclasses.TypeClasses // === First-Order Type Classes === @@ -1932,57 +1565,4 @@ static F3 of(Function>> f } // === Utilities === - -// Internal result type for instance selection -sealed interface ZeroOneMore { - record Zero() implements ZeroOneMore {} - - record One(A value) implements ZeroOneMore {} - - record More(List values) implements ZeroOneMore {} - - static ZeroOneMore of(List list) { - return switch (list.size()) { - case 0 -> new Zero<>(); - case 1 -> new One<>(list.getFirst()); - default -> new More<>(list); - }; - } -} - -// Internal list utilities -class Lists { - public static List map(List list, Function f) { - return list.stream().map(f).collect(Collectors.toList()); - } - - @SafeVarargs - public static List concat(List... lists) { - return Arrays.stream(lists).flatMap(List::stream).toList(); - } -} - -// Internal map utilities -class Maps { - public static Map merge(Map m1, Map m2) { - Map result = new HashMap<>(m1); - for (Map.Entry entry : m2.entrySet()) { - V existing = result.put(entry.getKey(), entry.getValue()); - if (existing != null && !existing.equals(entry.getValue())) { - throw new IllegalArgumentException("Duplicate key: " + entry.getKey()); - } - } - return result; - } -} - -// Internal function utilities -class Functions { - public static BiFunction flip(BiFunction f) { - return (b, a) -> f.apply(a, b); - } - - public static Function> curry(BiFunction f) { - return a -> b -> f.apply(a, b); - } -} +// ZeroOneMore, Lists, Maps, Functions are now in com.garciat.typeclasses.impl package diff --git a/src/main/java/com/garciat/typeclasses/TypeClasses.java b/src/main/java/com/garciat/typeclasses/TypeClasses.java new file mode 100644 index 0000000..3bbda80 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/TypeClasses.java @@ -0,0 +1,225 @@ +package com.garciat.typeclasses; + +import static com.garciat.typeclasses.api.TypeClass.Witness.Overlap.OVERLAPPABLE; +import static com.garciat.typeclasses.api.TypeClass.Witness.Overlap.OVERLAPPING; +import static java.lang.reflect.AccessFlag.PUBLIC; +import static java.lang.reflect.AccessFlag.STATIC; + +import com.garciat.typeclasses.api.Ctx; +import com.garciat.typeclasses.api.Ty; +import com.garciat.typeclasses.api.TypeClass; +import com.garciat.typeclasses.impl.Lists; +import com.garciat.typeclasses.impl.ZeroOneMore; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Central facility for type class witness resolution. + * + *

PUBLIC API: The {@link #witness} method is the main entry point for the library. + */ +public class TypeClasses { + /** + * Resolves and returns a witness (instance) of a type class for the given type. + * + *

This is the main entry point for using the type class system. It automatically finds and + * instantiates the appropriate type class instance based on the provided type token. + * + *

Example: + * + *

{@code
+   * Show> showListInt = TypeClasses.witness(new Ty<>() {});
+   * String result = showListInt.show(List.of(1, 2, 3));
+   * }
+ * + * @param the type class instance type + * @param ty the type token capturing the desired type class instance + * @param context optional context instances to use in resolution + * @return the resolved type class instance + * @throws WitnessResolutionException if no suitable instance can be found + */ + public static T witness(Ty ty, Ctx... context) { + return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) { + case Either.Left(SummonError error) -> + throw new WitnessResolutionException(error); + case Either.Right(Object instance) -> { + @SuppressWarnings("unchecked") + T typedInstance = (T) instance; + yield typedInstance; + } + }; + } + + private static List parseContext(Ctx[] context) { + return Arrays.stream(context) + .map(ctx -> new ContextInstance(ctx.instance(), ParsedType.parse(ctx.type()))) + .toList(); + } + + /** Exception thrown when a type class witness cannot be resolved. PUBLIC API. */ + public static class WitnessResolutionException extends RuntimeException { + private WitnessResolutionException(SummonError error) { + super(error.format()); + } + } + + private sealed interface SummonError { + record NotFound(ParsedType target) implements SummonError {} + + record Ambiguous(ParsedType target, List candidates) implements SummonError {} + + record Nested(ParsedType target, SummonError cause) implements SummonError {} + + default String format() { + return switch (this) { + case NotFound(ParsedType target) -> "No witness found for type: " + target.format(); + case Ambiguous(ParsedType target, List candidates) -> + "Ambiguous witnesses found for type: " + + target.format() + + "\nCandidates:\n" + + candidates.stream() + .map(c -> c.rule().toString()) + .collect(Collectors.joining("\n")) + .indent(2); + case Nested(ParsedType target, SummonError cause) -> + "While summoning witness for type: " + + target.format() + + "\nCaused by: " + + cause.format().indent(2); + }; + } + } + + private static Either summon( + ParsedType target, List context) { + return switch (ZeroOneMore.of(findCandidates(target, context))) { + case ZeroOneMore.One(Candidate(var rule, var requirements)) -> + summonAll(requirements, context) + .map(rule::instantiate) + .mapLeft(error -> new SummonError.Nested(target, error)); + case ZeroOneMore.Zero() -> Either.left(new SummonError.NotFound(target)); + case ZeroOneMore.More(var candidates) -> + Either.left(new SummonError.Ambiguous(target, candidates)); + }; + } + + private static Either> summonAll( + List targets, List context) { + return Either.traverse(targets, target -> summon(target, context)); + } + + private record Candidate(WitnessRule rule, List requirements) {} + + private static List findCandidates(ParsedType target, List context) { + return Stream.concat( + context.stream(), reduceOverlapping(findRules(target)).stream()) + .flatMap( + rule -> + rule + .tryMatch(target) + .map(requirements -> new Candidate(rule, requirements)) + .stream()) + .toList(); + } + + /** + * @implSpec
6.8.8.5. + * Overlapping instances + */ + private static List reduceOverlapping(List candidates) { + return candidates.stream() + .filter( + iX -> + candidates.stream().filter(iY -> iX != iY).noneMatch(iY -> isOverlappedBy(iX, iY))) + .toList(); + } + + private static boolean isOverlappedBy(InstanceConstructor iX, InstanceConstructor iY) { + return (iX.overlap() == OVERLAPPABLE || iY.overlap() == OVERLAPPING) + && isSubstitutionInstance(iX, iY) + && !isSubstitutionInstance(iY, iX); + } + + private static boolean isSubstitutionInstance( + InstanceConstructor base, InstanceConstructor reference) { + return Unification.unify(base.func().returnType(), reference.func().returnType()) + .fold(() -> false, map -> !map.isEmpty()); + } + + private static List findRules(ParsedType target) { + return switch (target) { + case ParsedType.App(var fun, var arg) -> Lists.concat(findRules(fun), findRules(arg)); + case ParsedType.Const(var java) -> rulesOf(java); + case ParsedType.Var(var java) -> List.of(); + case ParsedType.ArrayOf(var elem) -> List.of(); + case ParsedType.Primitive(var java) -> List.of(); + }; + } + + private static List rulesOf(Class cls) { + return Arrays.stream(cls.getDeclaredMethods()) + .filter(TypeClasses::isWitnessMethod) + .map(FuncType::parse) + .map(InstanceConstructor::new) + .toList(); + } + + private static boolean isWitnessMethod(Method m) { + return m.accessFlags().contains(PUBLIC) + && m.accessFlags().contains(STATIC) + && m.isAnnotationPresent(TypeClass.Witness.class); + } + + private sealed interface WitnessRule { + Maybe> tryMatch(ParsedType target); + + Object instantiate(List dependencies); + } + + private record ContextInstance(Object instance, ParsedType type) implements WitnessRule { + @Override + public Maybe> tryMatch(ParsedType target) { + return target.equals(type) ? Maybe.just(List.of()) : Maybe.nothing(); + } + + @Override + public Object instantiate(List dependencies) { + return instance; + } + + @Override + public String toString() { + return "context instance: " + type.format(); + } + } + + private record InstanceConstructor(FuncType func) implements WitnessRule { + public TypeClass.Witness.Overlap overlap() { + return func.java().getAnnotation(TypeClass.Witness.class).overlap(); + } + + @Override + public Maybe> tryMatch(ParsedType target) { + return Unification.unify(func.returnType(), target) + .map(map -> Unification.substituteAll(map, func.paramTypes())); + } + + @Override + public Object instantiate(List dependencies) { + try { + return func.java().invoke(null, dependencies.toArray()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return func.format(); + } + } +} diff --git a/src/main/java/com/garciat/typeclasses/api/Ctx.java b/src/main/java/com/garciat/typeclasses/api/Ctx.java new file mode 100644 index 0000000..c3d0e82 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/Ctx.java @@ -0,0 +1,51 @@ +package com.garciat.typeclasses.api; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Objects; + +/** + * Context token for capturing type class instances at runtime. + * + *

Usage: + * + *

{@code
+ * Show showString = ...;
+ * Ctx> ctx = new Ctx<>(showString) {};
+ * }
+ * + *

PUBLIC API: Used for passing explicit type class instances to witness resolution. + * + * @param the type class instance type + */ +public abstract class Ctx { + private final T instance; + + /** + * Constructs a context with the given instance. + * + * @param instance the type class instance + */ + public Ctx(T instance) { + this.instance = instance; + } + + /** + * Returns the instance. + * + * @return the type class instance + */ + public T instance() { + return instance; + } + + /** + * Returns the captured type. + * + * @return the Type object representing T + */ + public Type type() { + return Objects.requireNonNull( + ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); + } +} diff --git a/src/main/java/com/garciat/typeclasses/api/Kind.java b/src/main/java/com/garciat/typeclasses/api/Kind.java new file mode 100644 index 0000000..60d7a10 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/Kind.java @@ -0,0 +1,21 @@ +package com.garciat.typeclasses.api; + +/** + * Base interface for kind-level types, providing basic kind checking in Java. + * + *

This interface is used to represent type-level kinds, similar to kinds in Haskell's type + * system. + * + *

PUBLIC API: This is part of the library's public API. Users implementing custom data + * types will need to use this interface. + */ +public interface Kind { + /** Base interface for all kinds. */ + sealed interface Base permits KStar, KArr {} + + /** KStar represents the kind * (star) - the kind of proper types. */ + final class KStar implements Base {} + + /** KArr k represents the kind * -> k - the kind of type constructors. */ + final class KArr implements Base {} +} diff --git a/src/main/java/com/garciat/typeclasses/api/TApp.java b/src/main/java/com/garciat/typeclasses/api/TApp.java new file mode 100644 index 0000000..39309dc --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/TApp.java @@ -0,0 +1,14 @@ +package com.garciat.typeclasses.api; + +/** + * Full application of a unary type constructor. + * + *

TApp :: (* -> *) -> * -> * + * + *

PUBLIC API: This is part of the library's public API. Users will use this in type + * signatures. + * + * @param the type constructor tag + * @param the applied type argument + */ +public interface TApp>, A> extends Kind {} diff --git a/src/main/java/com/garciat/typeclasses/api/TPar.java b/src/main/java/com/garciat/typeclasses/api/TPar.java new file mode 100644 index 0000000..1f82832 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/TPar.java @@ -0,0 +1,15 @@ +package com.garciat.typeclasses.api; + +/** + * Partial application of a binary type constructor. + * + *

TPar :: (* -> * -> *) -> * -> (* -> *) + * + *

PUBLIC API: This is part of the library's public API. Users will use this in type + * signatures. + * + * @param the type constructor tag + * @param the first applied type argument + */ +public interface TPar>>, A> + extends Kind> {} diff --git a/src/main/java/com/garciat/typeclasses/api/TagBase.java b/src/main/java/com/garciat/typeclasses/api/TagBase.java new file mode 100644 index 0000000..f1e9415 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/TagBase.java @@ -0,0 +1,12 @@ +package com.garciat.typeclasses.api; + +/** + * Base class for type-level tags. Subclasses of this class represent type constructor tags used in + * higher-kinded type encoding. + * + *

PUBLIC API: This is part of the library's public API. Users implementing custom data + * types will need to extend this class. + * + * @param the kind of this tag + */ +public abstract class TagBase implements Kind {} diff --git a/src/main/java/com/garciat/typeclasses/api/Ty.java b/src/main/java/com/garciat/typeclasses/api/Ty.java new file mode 100644 index 0000000..affa0b1 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/Ty.java @@ -0,0 +1,31 @@ +package com.garciat.typeclasses.api; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Objects; + +/** + * Type token for capturing type information at runtime. + * + *

Usage: + * + *

{@code
+ * Show showString = TypeClasses.witness(new Ty>() {});
+ * }
+ * + *

PUBLIC API: This is the main interface users interact with to summon type class + * instances. + * + * @param the type being captured + */ +public interface Ty { + /** + * Returns the captured type. + * + * @return the Type object representing T + */ + default Type type() { + return Objects.requireNonNull( + ((ParameterizedType) getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]); + } +} diff --git a/src/main/java/com/garciat/typeclasses/api/TypeClass.java b/src/main/java/com/garciat/typeclasses/api/TypeClass.java new file mode 100644 index 0000000..c1252d8 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/api/TypeClass.java @@ -0,0 +1,38 @@ +package com.garciat.typeclasses.api; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Marks an interface as a type class. + * + *

Type classes are interfaces that define a set of operations that can be implemented for + * various types. The type class system uses compile-time and runtime reflection to automatically + * resolve instances. + * + *

PUBLIC API: This is part of the library's public API. Users define and implement type + * classes using this annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeClass { + /** Marks a method as a witness (instance) of a type class. */ + @Retention(RetentionPolicy.RUNTIME) + @interface Witness { + /** + * Specifies the overlap behavior for this witness. + * + * @return the overlap behavior + */ + Overlap overlap() default Overlap.NONE; + + /** Defines how instances can overlap with other instances. */ + enum Overlap { + /** No overlap allowed (default). */ + NONE, + /** This instance can overlap and take precedence over others. */ + OVERLAPPING, + /** This instance can be overlapped by others. */ + OVERLAPPABLE + } + } +} diff --git a/src/main/java/com/garciat/typeclasses/classes/package-info.java b/src/main/java/com/garciat/typeclasses/classes/package-info.java new file mode 100644 index 0000000..8c73bac --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/classes/package-info.java @@ -0,0 +1,21 @@ +/** + * Standard type class definitions. + * + *

This package contains interfaces annotated with {@code @TypeClass} that define standard type + * classes such as: + * + *

    + *
  • {@code Show} - Convert values to strings + *
  • {@code Eq} - Equality testing + *
  • {@code Ord} - Ordering comparisons + *
  • {@code Monoid} - Associative binary operations with identity + *
  • {@code Functor} - Mappable type constructors + *
  • {@code Applicative} - Application of functions in a context + *
  • {@code Monad} - Sequential composition of computations + *
  • {@code Foldable} - Structures that can be folded + *
  • {@code Traversable} - Structures that can be traversed + *
+ * + *

PUBLIC API: All type classes in this package are part of the public API. + */ +package com.garciat.typeclasses.classes; diff --git a/src/main/java/com/garciat/typeclasses/impl/Functions.java b/src/main/java/com/garciat/typeclasses/impl/Functions.java new file mode 100644 index 0000000..c85c9d6 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/impl/Functions.java @@ -0,0 +1,15 @@ +package com.garciat.typeclasses.impl; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** Internal function utilities. */ +public class Functions { + public static BiFunction flip(BiFunction f) { + return (b, a) -> f.apply(a, b); + } + + public static Function> curry(BiFunction f) { + return a -> b -> f.apply(a, b); + } +} diff --git a/src/main/java/com/garciat/typeclasses/impl/Lists.java b/src/main/java/com/garciat/typeclasses/impl/Lists.java new file mode 100644 index 0000000..2ae2324 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/impl/Lists.java @@ -0,0 +1,18 @@ +package com.garciat.typeclasses.impl; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** Internal list utilities. */ +public class Lists { + public static List map(List list, Function f) { + return list.stream().map(f).collect(Collectors.toList()); + } + + @SafeVarargs + public static List concat(List... lists) { + return Arrays.stream(lists).flatMap(List::stream).toList(); + } +} diff --git a/src/main/java/com/garciat/typeclasses/impl/Maps.java b/src/main/java/com/garciat/typeclasses/impl/Maps.java new file mode 100644 index 0000000..a1313c9 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/impl/Maps.java @@ -0,0 +1,18 @@ +package com.garciat.typeclasses.impl; + +import java.util.HashMap; +import java.util.Map; + +/** Internal map utilities. */ +public class Maps { + public static Map merge(Map m1, Map m2) { + Map result = new HashMap<>(m1); + for (Map.Entry entry : m2.entrySet()) { + V existing = result.put(entry.getKey(), entry.getValue()); + if (existing != null && !existing.equals(entry.getValue())) { + throw new IllegalArgumentException("Duplicate key: " + entry.getKey()); + } + } + return result; + } +} diff --git a/src/main/java/com/garciat/typeclasses/impl/ZeroOneMore.java b/src/main/java/com/garciat/typeclasses/impl/ZeroOneMore.java new file mode 100644 index 0000000..ae17241 --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/impl/ZeroOneMore.java @@ -0,0 +1,20 @@ +package com.garciat.typeclasses.impl; + +import java.util.List; + +/** Internal result type for instance selection. */ +public sealed interface ZeroOneMore { + record Zero() implements ZeroOneMore {} + + record One(A value) implements ZeroOneMore {} + + record More(List values) implements ZeroOneMore {} + + static ZeroOneMore of(List list) { + return switch (list.size()) { + case 0 -> new Zero<>(); + case 1 -> new One<>(list.getFirst()); + default -> new More<>(list); + }; + } +} diff --git a/src/main/java/com/garciat/typeclasses/types/package-info.java b/src/main/java/com/garciat/typeclasses/types/package-info.java new file mode 100644 index 0000000..9784e1d --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/types/package-info.java @@ -0,0 +1,19 @@ +/** + * Functional data types with type class instances. + * + *

This package contains data type implementations that provide instances for various type + * classes: + * + *

+ * + *

PUBLIC API: All types in this package are part of the public API. + */ +package com.garciat.typeclasses.types; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..8066b90 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/** + * Java Type Classes Library - A type class system for Java inspired by Haskell. + * + *

This module provides type classes and functional programming abstractions for Java. + * + *

Exported Packages

+ * + *
    + *
  • {@code com.garciat.typeclasses.api} - Core type class infrastructure (Kind, TApp, TPar, + * TagBase, TypeClass, Ty, Ctx) + *
  • {@code com.garciat.typeclasses} - All type classes and data types (for backward + * compatibility and current use) + *
+ * + *

The {@code com.garciat.typeclasses.impl} package contains internal implementation details and + * is not exported. The {@code classes} and {@code types} packages are reserved for future + * reorganization. + */ +module com.garciat.typeclasses { + // Export public API packages + exports com.garciat.typeclasses.api; + + // Export main package containing all type classes and data types + exports com.garciat.typeclasses; + +// impl package is NOT exported - it contains internal implementation details +// classes and types packages are reserved for future complete reorganization +} diff --git a/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java b/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java index 00de6fd..13a6ea9 100644 --- a/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java +++ b/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java @@ -3,6 +3,8 @@ import static com.garciat.typeclasses.TypeClasses.witness; import static org.junit.jupiter.api.Assertions.*; +import com.garciat.typeclasses.api.TApp; +import com.garciat.typeclasses.api.Ty; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; From 7b2207ae9226e425bef7dfde31fa2cd0aeb32240 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:44:36 +0000 Subject: [PATCH 07/11] Remove documentation and tests, focus on package structure only Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- README.md | 97 +--------------- .../java/com/garciat/typeclasses/Main.java | 9 -- .../com/garciat/typeclasses/TypeClasses.java | 25 ----- .../java/com/garciat/typeclasses/api/Ctx.java | 29 ----- .../com/garciat/typeclasses/api/Kind.java | 12 -- .../com/garciat/typeclasses/api/TApp.java | 11 -- .../com/garciat/typeclasses/api/TPar.java | 11 -- .../com/garciat/typeclasses/api/TagBase.java | 9 -- .../java/com/garciat/typeclasses/api/Ty.java | 19 ---- .../garciat/typeclasses/api/TypeClass.java | 20 ---- .../typeclasses/classes/package-info.java | 21 ---- .../com/garciat/typeclasses/package-info.java | 56 ---------- .../typeclasses/types/package-info.java | 19 ---- src/main/java/module-info.java | 24 ---- .../garciat/typeclasses/ExamplesTest.java} | 9 +- .../garciat/typeclasses/LibraryUsageTest.java | 105 ------------------ 16 files changed, 7 insertions(+), 469 deletions(-) delete mode 100644 src/main/java/com/garciat/typeclasses/classes/package-info.java delete mode 100644 src/main/java/com/garciat/typeclasses/package-info.java delete mode 100644 src/main/java/com/garciat/typeclasses/types/package-info.java rename src/{main/java/com/garciat/typeclasses/Examples.java => test/java/com/garciat/typeclasses/ExamplesTest.java} (93%) delete mode 100644 src/test/java/com/garciat/typeclasses/LibraryUsageTest.java diff --git a/README.md b/README.md index e055d37..e5ec91f 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,3 @@ -# Java Type Classes +Conventions: -A type class system for Java, inspired by Haskell's type classes. - -## Overview - -This library provides a way to define and use type classes in Java, enabling ad-hoc polymorphism through automatic instance resolution. It includes a rich set of predefined type classes and data types with higher-kinded type support. - -## Usage - -### Basic Example - -```java -import static com.garciat.typeclasses.TypeClasses.witness; - -// Automatically resolve a Show instance for List -Show> showListInt = witness(new Ty<>() {}); -String result = showListInt.show(List.of(1, 2, 3)); -// result: "[1, 2, 3]" -``` - -### Core Type Classes - -The library provides several built-in type classes: - -- **Show** - Convert values to strings -- **Eq** - Equality testing -- **Ord** - Ordering comparisons -- **Monoid** - Associative binary operations with identity -- **Functor** - Mappable type constructors -- **Applicative** - Application of functions in a context -- **Monad** - Sequential composition of computations -- **Foldable** - Structures that can be folded -- **Traversable** - Structures that can be traversed - -### Data Types - -The library includes functional data types with type class instances: - -- **Maybe** - Optional values (`Just` or `Nothing`) -- **Either** - Sum types (`Left` or `Right`) -- **JavaList** - List with type class instances -- **FwdList** - Functional forward list -- **Parser** - Parser combinators -- **State** - State monad - -### Higher-Kinded Types - -The library supports higher-kinded types through a defunctionalization encoding: - -```java -// Work with Functors abstractly -Functor functorMaybe = witness(new Ty<>() {}); -TApp maybeInt = Maybe.just(42); -TApp maybeStr = functorMaybe.map(Object::toString, maybeInt); -``` - -## Examples - -See the `Examples` class for comprehensive usage examples: - -```bash -mvn compile exec:java -Dexec.mainClass="com.garciat.typeclasses.Examples" -``` - -## API Structure - -### Public API - -The main entry point is `TypeClasses.witness()` which resolves type class instances. All type classes (marked with `@TypeClass`) and data types are part of the public API. - -Key public components: -- `TypeClasses.witness()` - Resolve type class instances -- `Ty` - Type token for capturing types -- `Ctx` - Context token for explicit instances -- `@TypeClass` - Annotation for defining type classes -- `Kind`, `TApp`, `TPar`, `TagBase` - Higher-kinded type infrastructure - -### Internal Implementation - -Internal implementation details (parsing, unification, witness resolution algorithms) are package-private and should not be relied upon by library users. - -## Building - -```bash -mvn clean compile test -``` - -## Conventions - -- Use google-java-format for code formatting -- Java 21 is required - -## License - -See repository for license information. +- Use google-java-format diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index ceba9e7..4a3c17b 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -36,15 +36,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -/** - * Core type class infrastructure for Java. - * - *

This class has been retained for backward compatibility but is no longer the main entry point. - * The library should be accessed through the public type classes and data types. - * - * @deprecated Use {@link Examples} for demonstration code, or the individual type classes directly. - */ -@Deprecated public final class Main { private Main() {} } diff --git a/src/main/java/com/garciat/typeclasses/TypeClasses.java b/src/main/java/com/garciat/typeclasses/TypeClasses.java index 3bbda80..bafda6d 100644 --- a/src/main/java/com/garciat/typeclasses/TypeClasses.java +++ b/src/main/java/com/garciat/typeclasses/TypeClasses.java @@ -16,31 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -/** - * Central facility for type class witness resolution. - * - *

PUBLIC API: The {@link #witness} method is the main entry point for the library. - */ public class TypeClasses { - /** - * Resolves and returns a witness (instance) of a type class for the given type. - * - *

This is the main entry point for using the type class system. It automatically finds and - * instantiates the appropriate type class instance based on the provided type token. - * - *

Example: - * - *

{@code
-   * Show> showListInt = TypeClasses.witness(new Ty<>() {});
-   * String result = showListInt.show(List.of(1, 2, 3));
-   * }
- * - * @param the type class instance type - * @param ty the type token capturing the desired type class instance - * @param context optional context instances to use in resolution - * @return the resolved type class instance - * @throws WitnessResolutionException if no suitable instance can be found - */ public static T witness(Ty ty, Ctx... context) { return switch (summon(ParsedType.parse(ty.type()), parseContext(context))) { case Either.Left(SummonError error) -> @@ -59,7 +35,6 @@ private static List parseContext(Ctx[] context) { .toList(); } - /** Exception thrown when a type class witness cannot be resolved. PUBLIC API. */ public static class WitnessResolutionException extends RuntimeException { private WitnessResolutionException(SummonError error) { super(error.format()); diff --git a/src/main/java/com/garciat/typeclasses/api/Ctx.java b/src/main/java/com/garciat/typeclasses/api/Ctx.java index c3d0e82..7125940 100644 --- a/src/main/java/com/garciat/typeclasses/api/Ctx.java +++ b/src/main/java/com/garciat/typeclasses/api/Ctx.java @@ -4,46 +4,17 @@ import java.lang.reflect.Type; import java.util.Objects; -/** - * Context token for capturing type class instances at runtime. - * - *

Usage: - * - *

{@code
- * Show showString = ...;
- * Ctx> ctx = new Ctx<>(showString) {};
- * }
- * - *

PUBLIC API: Used for passing explicit type class instances to witness resolution. - * - * @param the type class instance type - */ public abstract class Ctx { private final T instance; - /** - * Constructs a context with the given instance. - * - * @param instance the type class instance - */ public Ctx(T instance) { this.instance = instance; } - /** - * Returns the instance. - * - * @return the type class instance - */ public T instance() { return instance; } - /** - * Returns the captured type. - * - * @return the Type object representing T - */ public Type type() { return Objects.requireNonNull( ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); diff --git a/src/main/java/com/garciat/typeclasses/api/Kind.java b/src/main/java/com/garciat/typeclasses/api/Kind.java index 60d7a10..ed31300 100644 --- a/src/main/java/com/garciat/typeclasses/api/Kind.java +++ b/src/main/java/com/garciat/typeclasses/api/Kind.java @@ -1,21 +1,9 @@ package com.garciat.typeclasses.api; -/** - * Base interface for kind-level types, providing basic kind checking in Java. - * - *

This interface is used to represent type-level kinds, similar to kinds in Haskell's type - * system. - * - *

PUBLIC API: This is part of the library's public API. Users implementing custom data - * types will need to use this interface. - */ public interface Kind { - /** Base interface for all kinds. */ sealed interface Base permits KStar, KArr {} - /** KStar represents the kind * (star) - the kind of proper types. */ final class KStar implements Base {} - /** KArr k represents the kind * -> k - the kind of type constructors. */ final class KArr implements Base {} } diff --git a/src/main/java/com/garciat/typeclasses/api/TApp.java b/src/main/java/com/garciat/typeclasses/api/TApp.java index 39309dc..be227bb 100644 --- a/src/main/java/com/garciat/typeclasses/api/TApp.java +++ b/src/main/java/com/garciat/typeclasses/api/TApp.java @@ -1,14 +1,3 @@ package com.garciat.typeclasses.api; -/** - * Full application of a unary type constructor. - * - *

TApp :: (* -> *) -> * -> * - * - *

PUBLIC API: This is part of the library's public API. Users will use this in type - * signatures. - * - * @param the type constructor tag - * @param the applied type argument - */ public interface TApp>, A> extends Kind {} diff --git a/src/main/java/com/garciat/typeclasses/api/TPar.java b/src/main/java/com/garciat/typeclasses/api/TPar.java index 1f82832..eedc920 100644 --- a/src/main/java/com/garciat/typeclasses/api/TPar.java +++ b/src/main/java/com/garciat/typeclasses/api/TPar.java @@ -1,15 +1,4 @@ package com.garciat.typeclasses.api; -/** - * Partial application of a binary type constructor. - * - *

TPar :: (* -> * -> *) -> * -> (* -> *) - * - *

PUBLIC API: This is part of the library's public API. Users will use this in type - * signatures. - * - * @param the type constructor tag - * @param the first applied type argument - */ public interface TPar>>, A> extends Kind> {} diff --git a/src/main/java/com/garciat/typeclasses/api/TagBase.java b/src/main/java/com/garciat/typeclasses/api/TagBase.java index f1e9415..9dcb1de 100644 --- a/src/main/java/com/garciat/typeclasses/api/TagBase.java +++ b/src/main/java/com/garciat/typeclasses/api/TagBase.java @@ -1,12 +1,3 @@ package com.garciat.typeclasses.api; -/** - * Base class for type-level tags. Subclasses of this class represent type constructor tags used in - * higher-kinded type encoding. - * - *

PUBLIC API: This is part of the library's public API. Users implementing custom data - * types will need to extend this class. - * - * @param the kind of this tag - */ public abstract class TagBase implements Kind {} diff --git a/src/main/java/com/garciat/typeclasses/api/Ty.java b/src/main/java/com/garciat/typeclasses/api/Ty.java index affa0b1..6c2988f 100644 --- a/src/main/java/com/garciat/typeclasses/api/Ty.java +++ b/src/main/java/com/garciat/typeclasses/api/Ty.java @@ -4,26 +4,7 @@ import java.lang.reflect.Type; import java.util.Objects; -/** - * Type token for capturing type information at runtime. - * - *

Usage: - * - *

{@code
- * Show showString = TypeClasses.witness(new Ty>() {});
- * }
- * - *

PUBLIC API: This is the main interface users interact with to summon type class - * instances. - * - * @param the type being captured - */ public interface Ty { - /** - * Returns the captured type. - * - * @return the Type object representing T - */ default Type type() { return Objects.requireNonNull( ((ParameterizedType) getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]); diff --git a/src/main/java/com/garciat/typeclasses/api/TypeClass.java b/src/main/java/com/garciat/typeclasses/api/TypeClass.java index c1252d8..c264b03 100644 --- a/src/main/java/com/garciat/typeclasses/api/TypeClass.java +++ b/src/main/java/com/garciat/typeclasses/api/TypeClass.java @@ -3,35 +3,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -/** - * Marks an interface as a type class. - * - *

Type classes are interfaces that define a set of operations that can be implemented for - * various types. The type class system uses compile-time and runtime reflection to automatically - * resolve instances. - * - *

PUBLIC API: This is part of the library's public API. Users define and implement type - * classes using this annotation. - */ @Retention(RetentionPolicy.RUNTIME) public @interface TypeClass { - /** Marks a method as a witness (instance) of a type class. */ @Retention(RetentionPolicy.RUNTIME) @interface Witness { - /** - * Specifies the overlap behavior for this witness. - * - * @return the overlap behavior - */ Overlap overlap() default Overlap.NONE; - /** Defines how instances can overlap with other instances. */ enum Overlap { - /** No overlap allowed (default). */ NONE, - /** This instance can overlap and take precedence over others. */ OVERLAPPING, - /** This instance can be overlapped by others. */ OVERLAPPABLE } } diff --git a/src/main/java/com/garciat/typeclasses/classes/package-info.java b/src/main/java/com/garciat/typeclasses/classes/package-info.java deleted file mode 100644 index 8c73bac..0000000 --- a/src/main/java/com/garciat/typeclasses/classes/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Standard type class definitions. - * - *

This package contains interfaces annotated with {@code @TypeClass} that define standard type - * classes such as: - * - *

    - *
  • {@code Show} - Convert values to strings - *
  • {@code Eq} - Equality testing - *
  • {@code Ord} - Ordering comparisons - *
  • {@code Monoid} - Associative binary operations with identity - *
  • {@code Functor} - Mappable type constructors - *
  • {@code Applicative} - Application of functions in a context - *
  • {@code Monad} - Sequential composition of computations - *
  • {@code Foldable} - Structures that can be folded - *
  • {@code Traversable} - Structures that can be traversed - *
- * - *

PUBLIC API: All type classes in this package are part of the public API. - */ -package com.garciat.typeclasses.classes; diff --git a/src/main/java/com/garciat/typeclasses/package-info.java b/src/main/java/com/garciat/typeclasses/package-info.java deleted file mode 100644 index 0cad5ba..0000000 --- a/src/main/java/com/garciat/typeclasses/package-info.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Java Type Classes Library - * - *

This library provides a type class system for Java, inspired by Haskell's type classes. It - * allows you to define type classes (interfaces with @TypeClass annotation) and automatically - * resolve instances for various types. - * - *

Public API

- * - *

The main entry point is {@link com.garciat.typeclasses.TypeClasses#witness}, which resolves - * type class instances: - * - *

{@code
- * Show> showListInt = TypeClasses.witness(new Ty<>() {});
- * String result = showListInt.show(List.of(1, 2, 3));
- * }
- * - *

Core Type Classes

- * - *
    - *
  • {@link com.garciat.typeclasses.Show} - Convert values to strings - *
  • {@link com.garciat.typeclasses.Eq} - Equality testing - *
  • {@link com.garciat.typeclasses.Ord} - Ordering comparisons - *
  • {@link com.garciat.typeclasses.Monoid} - Associative binary operations with identity - *
  • {@link com.garciat.typeclasses.Functor} - Mappable type constructors - *
  • {@link com.garciat.typeclasses.Applicative} - Application of functions in a context - *
  • {@link com.garciat.typeclasses.Monad} - Sequential composition of computations - *
  • {@link com.garciat.typeclasses.Foldable} - Structures that can be folded - *
  • {@link com.garciat.typeclasses.Traversable} - Structures that can be traversed - *
- * - *

Data Types

- * - *
    - *
  • {@link com.garciat.typeclasses.Maybe} - Optional values - *
  • {@link com.garciat.typeclasses.Either} - Sum types (Left or Right) - *
  • {@link com.garciat.typeclasses.JavaList} - List with type class instances - *
  • {@link com.garciat.typeclasses.FwdList} - Functional forward list - *
  • {@link com.garciat.typeclasses.Parser} - Parser combinators - *
  • {@link com.garciat.typeclasses.State} - State monad - *
- * - *

Type System Infrastructure

- * - *
    - *
  • {@link com.garciat.typeclasses.Kind} - Kind system for higher-kinded types - *
  • {@link com.garciat.typeclasses.TApp} - Type application - *
  • {@link com.garciat.typeclasses.TPar} - Partial type application - *
  • {@link com.garciat.typeclasses.TagBase} - Base class for type tags - *
- * - *

Examples

- * - *

See {@link com.garciat.typeclasses.Examples} for usage examples. - */ -package com.garciat.typeclasses; diff --git a/src/main/java/com/garciat/typeclasses/types/package-info.java b/src/main/java/com/garciat/typeclasses/types/package-info.java deleted file mode 100644 index 9784e1d..0000000 --- a/src/main/java/com/garciat/typeclasses/types/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Functional data types with type class instances. - * - *

This package contains data type implementations that provide instances for various type - * classes: - * - *

    - *
  • {@code Maybe} - Optional values (Just or Nothing) - *
  • {@code Either} - Sum types (Left or Right) - *
  • {@code JavaList} - List with type class instances - *
  • {@code FwdList} - Functional forward list - *
  • {@code Parser} - Parser combinators - *
  • {@code State} - State monad - *
  • {@code Pair} - Product type of two values - *
- * - *

PUBLIC API: All types in this package are part of the public API. - */ -package com.garciat.typeclasses.types; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8066b90..43919fd 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,28 +1,4 @@ -/** - * Java Type Classes Library - A type class system for Java inspired by Haskell. - * - *

This module provides type classes and functional programming abstractions for Java. - * - *

Exported Packages

- * - *
    - *
  • {@code com.garciat.typeclasses.api} - Core type class infrastructure (Kind, TApp, TPar, - * TagBase, TypeClass, Ty, Ctx) - *
  • {@code com.garciat.typeclasses} - All type classes and data types (for backward - * compatibility and current use) - *
- * - *

The {@code com.garciat.typeclasses.impl} package contains internal implementation details and - * is not exported. The {@code classes} and {@code types} packages are reserved for future - * reorganization. - */ module com.garciat.typeclasses { - // Export public API packages exports com.garciat.typeclasses.api; - - // Export main package containing all type classes and data types exports com.garciat.typeclasses; - -// impl package is NOT exported - it contains internal implementation details -// classes and types packages are reserved for future complete reorganization } diff --git a/src/main/java/com/garciat/typeclasses/Examples.java b/src/test/java/com/garciat/typeclasses/ExamplesTest.java similarity index 93% rename from src/main/java/com/garciat/typeclasses/Examples.java rename to src/test/java/com/garciat/typeclasses/ExamplesTest.java index e78d8b1..8fe6df5 100644 --- a/src/main/java/com/garciat/typeclasses/Examples.java +++ b/src/test/java/com/garciat/typeclasses/ExamplesTest.java @@ -8,12 +8,13 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +import org.junit.jupiter.api.Test; -/** Demonstration of the java-type-classes library features. */ -public final class Examples { - private Examples() {} +/** Tests demonstrating the java-type-classes library features. */ +final class ExamplesTest { - public static void main(String[] args) { + @Test + void examples() { System.out.println(Show.show(witness(new Ty<>() {}), new int[] {1, 2, 3, 4, 5})); System.out.println(Show.show(witness(new Ty<>() {}), new Integer[] {1, 2, 3, 4, 5})); diff --git a/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java b/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java deleted file mode 100644 index 13a6ea9..0000000 --- a/src/test/java/com/garciat/typeclasses/LibraryUsageTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.garciat.typeclasses; - -import static com.garciat.typeclasses.TypeClasses.witness; -import static org.junit.jupiter.api.Assertions.*; - -import com.garciat.typeclasses.api.TApp; -import com.garciat.typeclasses.api.Ty; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.Test; - -/** Tests demonstrating the library's public API usage. */ -final class LibraryUsageTest { - - @Test - void canResolveShowForBasicTypes() { - Show showInt = witness(new Ty<>() {}); - assertEquals("42", showInt.show(42)); - - Show showString = witness(new Ty<>() {}); - assertEquals("\"hello\"", showString.show("hello")); - } - - @Test - void canResolveShowForComplexTypes() { - Show> showListInt = witness(new Ty<>() {}); - String result = showListInt.show(List.of(1, 2, 3)); - assertEquals("[1, 2, 3]", result); - } - - @Test - void canResolveShowForNestedTypes() { - Show>> showOptListStr = witness(new Ty<>() {}); - String result = showOptListStr.show(Optional.of(List.of("a", "b"))); - assertEquals("Some([\"a\", \"b\"])", result); - } - - @Test - void canResolveEqForBasicTypes() { - Eq eqInt = witness(new Ty<>() {}); - assertTrue(eqInt.eq(42, 42)); - assertFalse(eqInt.eq(42, 43)); - } - - @Test - void canResolveEqForComplexTypes() { - Eq> eqListStr = witness(new Ty<>() {}); - assertTrue(eqListStr.eq(List.of("a", "b"), List.of("a", "b"))); - assertFalse(eqListStr.eq(List.of("a", "b"), List.of("a", "c"))); - } - - @Test - void canResolveOrdForBasicTypes() { - Ord ordInt = witness(new Ty<>() {}); - assertEquals(Ordering.LT, ordInt.compare(1, 2)); - assertEquals(Ordering.EQ, ordInt.compare(2, 2)); - assertEquals(Ordering.GT, ordInt.compare(3, 2)); - } - - @Test - void canResolveMonoidForString() { - Monoid monoidString = witness(new Ty<>() {}); - assertEquals("", monoidString.identity()); - assertEquals("ab", monoidString.combine("a", "b")); - } - - @Test - void canUseMonoidCombineAll() { - List strings = List.of("Hello", " ", "World"); - String result = Monoid.combineAll(witness(new Ty<>() {}), strings); - assertEquals("Hello World", result); - } - - @Test - void canUseFunctorMap() { - Functor functorMaybe = witness(new Ty<>() {}); - TApp maybeInt = Maybe.just(42); - TApp maybeStr = functorMaybe.map(Object::toString, maybeInt); - assertEquals("42", Maybe.unwrap(maybeStr).fold(() -> null, x -> x)); - } - - @Test - void canUseApplicativePure() { - Applicative appMaybe = witness(new Ty<>() {}); - TApp maybeInt = appMaybe.pure(42); - Integer unwrapped = Maybe.unwrap(maybeInt).fold(() -> null, x -> x); - assertEquals(42, unwrapped.intValue()); - } - - @Test - void canUseMonadFlatMap() { - Monad monadMaybe = witness(new Ty<>() {}); - TApp maybeInt = Maybe.just(42); - TApp result = monadMaybe.flatMap(x -> Maybe.just(x * 2), maybeInt); - Integer unwrapped = Maybe.unwrap(result).fold(() -> null, x -> x); - assertEquals(84, unwrapped.intValue()); - } - - @Test - void canUseFoldableLength() { - Foldable foldableFwdList = witness(new Ty<>() {}); - FwdList list = FwdList.of(1, 2, 3, 4, 5); - assertEquals(5, (int) foldableFwdList.length(list)); - } -} From 2c81f27e2fc0e064c709666d669f1813f85e32ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:59:14 +0000 Subject: [PATCH 08/11] Move HKT types from api to api.hkt package Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- src/main/java/com/garciat/typeclasses/Main.java | 15 ++++++--------- .../garciat/typeclasses/api/{ => hkt}/Kind.java | 2 +- .../garciat/typeclasses/api/{ => hkt}/TApp.java | 2 +- .../garciat/typeclasses/api/{ => hkt}/TPar.java | 2 +- .../typeclasses/api/{ => hkt}/TagBase.java | 2 +- src/main/java/module-info.java | 1 + 6 files changed, 11 insertions(+), 13 deletions(-) rename src/main/java/com/garciat/typeclasses/api/{ => hkt}/Kind.java (82%) rename src/main/java/com/garciat/typeclasses/api/{ => hkt}/TApp.java (69%) rename src/main/java/com/garciat/typeclasses/api/{ => hkt}/TPar.java (74%) rename src/main/java/com/garciat/typeclasses/api/{ => hkt}/TagBase.java (64%) diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 4a3c17b..42e4f4b 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -6,13 +6,13 @@ import static com.garciat.typeclasses.impl.Functions.flip; import static java.util.function.Function.identity; -import com.garciat.typeclasses.api.Kind; -import com.garciat.typeclasses.api.Kind.KArr; -import com.garciat.typeclasses.api.Kind.KStar; -import com.garciat.typeclasses.api.TApp; -import com.garciat.typeclasses.api.TPar; -import com.garciat.typeclasses.api.TagBase; import com.garciat.typeclasses.api.TypeClass; +import com.garciat.typeclasses.api.hkt.Kind; +import com.garciat.typeclasses.api.hkt.Kind.KArr; +import com.garciat.typeclasses.api.hkt.Kind.KStar; +import com.garciat.typeclasses.api.hkt.TApp; +import com.garciat.typeclasses.api.hkt.TPar; +import com.garciat.typeclasses.api.hkt.TagBase; import com.garciat.typeclasses.impl.Lists; import com.garciat.typeclasses.impl.Maps; import java.lang.reflect.GenericArrayType; @@ -40,9 +40,6 @@ public final class Main { private Main() {} } -// ==== Type System ==== -// Kind, TApp, TPar, TagBase are now in com.garciat.typeclasses.api package - // Internal type parsing sealed interface ParsedType { record Var(TypeVariable java) implements ParsedType {} diff --git a/src/main/java/com/garciat/typeclasses/api/Kind.java b/src/main/java/com/garciat/typeclasses/api/hkt/Kind.java similarity index 82% rename from src/main/java/com/garciat/typeclasses/api/Kind.java rename to src/main/java/com/garciat/typeclasses/api/hkt/Kind.java index ed31300..9f62ff6 100644 --- a/src/main/java/com/garciat/typeclasses/api/Kind.java +++ b/src/main/java/com/garciat/typeclasses/api/hkt/Kind.java @@ -1,4 +1,4 @@ -package com.garciat.typeclasses.api; +package com.garciat.typeclasses.api.hkt; public interface Kind { sealed interface Base permits KStar, KArr {} diff --git a/src/main/java/com/garciat/typeclasses/api/TApp.java b/src/main/java/com/garciat/typeclasses/api/hkt/TApp.java similarity index 69% rename from src/main/java/com/garciat/typeclasses/api/TApp.java rename to src/main/java/com/garciat/typeclasses/api/hkt/TApp.java index be227bb..1292ceb 100644 --- a/src/main/java/com/garciat/typeclasses/api/TApp.java +++ b/src/main/java/com/garciat/typeclasses/api/hkt/TApp.java @@ -1,3 +1,3 @@ -package com.garciat.typeclasses.api; +package com.garciat.typeclasses.api.hkt; public interface TApp>, A> extends Kind {} diff --git a/src/main/java/com/garciat/typeclasses/api/TPar.java b/src/main/java/com/garciat/typeclasses/api/hkt/TPar.java similarity index 74% rename from src/main/java/com/garciat/typeclasses/api/TPar.java rename to src/main/java/com/garciat/typeclasses/api/hkt/TPar.java index eedc920..3f383e3 100644 --- a/src/main/java/com/garciat/typeclasses/api/TPar.java +++ b/src/main/java/com/garciat/typeclasses/api/hkt/TPar.java @@ -1,4 +1,4 @@ -package com.garciat.typeclasses.api; +package com.garciat.typeclasses.api.hkt; public interface TPar>>, A> extends Kind> {} diff --git a/src/main/java/com/garciat/typeclasses/api/TagBase.java b/src/main/java/com/garciat/typeclasses/api/hkt/TagBase.java similarity index 64% rename from src/main/java/com/garciat/typeclasses/api/TagBase.java rename to src/main/java/com/garciat/typeclasses/api/hkt/TagBase.java index 9dcb1de..324ca28 100644 --- a/src/main/java/com/garciat/typeclasses/api/TagBase.java +++ b/src/main/java/com/garciat/typeclasses/api/hkt/TagBase.java @@ -1,3 +1,3 @@ -package com.garciat.typeclasses.api; +package com.garciat.typeclasses.api.hkt; public abstract class TagBase implements Kind {} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 43919fd..0a77a78 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,5 @@ module com.garciat.typeclasses { exports com.garciat.typeclasses.api; + exports com.garciat.typeclasses.api.hkt; exports com.garciat.typeclasses; } From de5d0c6255a6226723801302d0db9ea3ac98c0ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:02:17 +0000 Subject: [PATCH 09/11] Extract Ordering enum to classes package and export it Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- src/main/java/com/garciat/typeclasses/Main.java | 7 +------ .../java/com/garciat/typeclasses/classes/Ordering.java | 7 +++++++ src/main/java/module-info.java | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/garciat/typeclasses/classes/Ordering.java diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 42e4f4b..32697a8 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -13,6 +13,7 @@ import com.garciat.typeclasses.api.hkt.TApp; import com.garciat.typeclasses.api.hkt.TPar; import com.garciat.typeclasses.api.hkt.TagBase; +import com.garciat.typeclasses.classes.Ordering; import com.garciat.typeclasses.impl.Lists; import com.garciat.typeclasses.impl.Maps; import java.lang.reflect.GenericArrayType; @@ -331,12 +332,6 @@ static Eq> mapEq(Eq eqK, Eq eqV) { } } -enum Ordering { - LT, - EQ, - GT -} - @TypeClass interface Ord extends Eq { Ordering compare(A a1, A a2); diff --git a/src/main/java/com/garciat/typeclasses/classes/Ordering.java b/src/main/java/com/garciat/typeclasses/classes/Ordering.java new file mode 100644 index 0000000..48b261d --- /dev/null +++ b/src/main/java/com/garciat/typeclasses/classes/Ordering.java @@ -0,0 +1,7 @@ +package com.garciat.typeclasses.classes; + +public enum Ordering { + LT, + EQ, + GT +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0a77a78..3001540 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,5 +1,6 @@ module com.garciat.typeclasses { exports com.garciat.typeclasses.api; exports com.garciat.typeclasses.api.hkt; + exports com.garciat.typeclasses.classes; exports com.garciat.typeclasses; } From 004c8bf2ff0730f00823bd8780411b2b33658e2e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:13:54 +0000 Subject: [PATCH 10/11] Remove empty Main class wrapper from Main.java Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- src/main/java/com/garciat/typeclasses/Main.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/Main.java index 32697a8..ca565b3 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/Main.java @@ -37,9 +37,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public final class Main { - private Main() {} -} // Internal type parsing sealed interface ParsedType { From 8b896995fe285ae8242e779d2b25ddcf3fb13ac0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:37:48 +0000 Subject: [PATCH 11/11] Rename Main.java to TypeDefinitions.java Co-authored-by: Garciat <118277+Garciat@users.noreply.github.com> --- .../com/garciat/typeclasses/{Main.java => TypeDefinitions.java} | 2 ++ 1 file changed, 2 insertions(+) rename src/main/java/com/garciat/typeclasses/{Main.java => TypeDefinitions.java} (99%) diff --git a/src/main/java/com/garciat/typeclasses/Main.java b/src/main/java/com/garciat/typeclasses/TypeDefinitions.java similarity index 99% rename from src/main/java/com/garciat/typeclasses/Main.java rename to src/main/java/com/garciat/typeclasses/TypeDefinitions.java index ca565b3..00001ff 100644 --- a/src/main/java/com/garciat/typeclasses/Main.java +++ b/src/main/java/com/garciat/typeclasses/TypeDefinitions.java @@ -1,3 +1,5 @@ +// This file contains all type class and data type definitions +// TODO: Extract to individual files in classes/ and types/ packages package com.garciat.typeclasses; import static com.garciat.typeclasses.TyEq.refl;