Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions core/src/main/java/com/garciat/typeclasses/TypeClasses.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,65 @@
package com.garciat.typeclasses;

import com.garciat.typeclasses.api.Lazy;
import com.garciat.typeclasses.api.Ty;
import com.garciat.typeclasses.impl.Match;
import com.garciat.typeclasses.impl.ParsedType;
import com.garciat.typeclasses.impl.Resolution;
import com.garciat.typeclasses.impl.utils.Either;
import com.garciat.typeclasses.runtime.Runtime;
import com.garciat.typeclasses.runtime.RuntimeWitnessSystem;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public final class TypeClasses {
private TypeClasses() {}

public static <T> T witness(Ty<T> ty) {
Object instance =
switch (RuntimeWitnessSystem.resolve(ty.type(), TypeClasses::invoke)) {
Resolution.Result<Runtime.Method, Runtime.Var, Runtime.Const, Runtime.Prim> methodTree =
switch (RuntimeWitnessSystem.resolve(ty.type())) {
case Either.Right(var r) -> r;
case Either.Left(var error) -> throw new WitnessResolutionException(error.format());
};

Object instance = walk(new HashMap<>(), methodTree);

@SuppressWarnings("unchecked")
T typedInstance = (T) instance;
return typedInstance;
}

private static Object walk(
Map<ParsedType<Runtime.Var, Runtime.Const, Runtime.Prim>, Object> cache,
Resolution.Result<Runtime.Method, Runtime.Var, Runtime.Const, Runtime.Prim> tree) {
return switch (tree) {
case Resolution.Result.Node(var match, var dependencies) -> {
Object[] args = dependencies.stream().map(dep -> walk(cache, dep)).toArray();

Object instance = invoke(match, args);

cache.put(match.witnessType(), instance);

yield instance;
}
case Resolution.Result.LazyLookup(var target) ->
(Lazy<Object>)
() ->
Optional.ofNullable(cache.get(target))
.orElseThrow(
() ->
new WitnessResolutionException(
"BUG: expected cached instance for %s"
.formatted(target.format())));
case Resolution.Result.LazyWrap(var under) -> (Lazy<Object>) () -> walk(cache, under);
};
}

private static Object invoke(
Match<Runtime.Method, Runtime.Var, Runtime.Const, Runtime.Prim> match, List<Object> args) {
Match<Runtime.Method, Runtime.Var, Runtime.Const, Runtime.Prim> match, Object[] args) {
try {
return match.ctor().method().java().invoke(null, args.toArray());
return match.ctor().method().java().invoke(null, args);
} catch (IllegalAccessException e) {
throw new IllegalStateException("BUG: expected witness constructor method to be public", e);
} catch (InvocationTargetException e) {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/com/garciat/typeclasses/api/Lazy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.garciat.typeclasses.api;

public interface Lazy<A> {
A get();
}
10 changes: 10 additions & 0 deletions core/src/main/java/com/garciat/typeclasses/api/Out.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.garciat.typeclasses.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_PARAMETER})
public @interface Out {}
23 changes: 20 additions & 3 deletions core/src/main/java/com/garciat/typeclasses/impl/ParsedType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,50 @@
import java.util.List;

public sealed interface ParsedType<V, C, P> {
record Var<V, C, P>(V repr) implements ParsedType<V, C, P> {}
record Var<V, C, P>(TyParam<V> ref) implements ParsedType<V, C, P> {}

record Out<V, C, P>(ParsedType<V, C, P> under) implements ParsedType<V, C, P> {}

record App<V, C, P>(ParsedType<V, C, P> fun, ParsedType<V, C, P> arg)
implements ParsedType<V, C, P> {}

record ArrayOf<V, C, P>(ParsedType<V, C, P> elementType) implements ParsedType<V, C, P> {}

record Const<V, C, P>(C repr, List<Var<V, C, P>> typeParams) implements ParsedType<V, C, P> {}
record Const<V, C, P>(C repr, List<TyParam<V>> typeParams) implements ParsedType<V, C, P> {}

record Primitive<V, C, P>(P repr) implements ParsedType<V, C, P> {}

record Wildcard<V, C, P>() implements ParsedType<V, C, P> {}

record Lazy<V, C, P>(ParsedType<V, C, P> under) implements ParsedType<V, C, P> {}

record TyParam<V>(V repr, boolean isOut) {
public <C, P> ParsedType<V, C, P> wrapOut(ParsedType<V, C, P> under) {
return isOut ? new Out<>(under) : under;
}

@Override
public String toString() {
return (isOut ? "&" : "") + repr;
}
}

default String format() {
return switch (this) {
case Var(var repr) -> repr.toString();
case Out(var under) -> "Out<" + under.format() + ">";
case Const(var repr, var typeParams) ->
repr.toString()
+ typeParams.stream()
.map(ParsedType::format)
.map(TyParam::toString)
.reduce((a, b) -> a + ", " + b)
.map(s -> "[" + s + "]")
.orElse("");
case App(var fun, var arg) -> fun.format() + "(" + arg.format() + ")";
case ArrayOf(var elem) -> elem.format() + "[]";
case Primitive(var repr) -> repr.toString();
case Wildcard() -> "?";
case Lazy(var repr) -> "Lazy<" + repr.format() + ">";
};
}
}
Loading