Last active
May 7, 2017 21:40
-
-
Save jbgi/09e0ac4ff49868cae938afbbd59bea58 to your computer and use it in GitHub Desktop.
Automatic derivation of FJ Equal/Hash/Show/Ord "type classes" via derive4j processor-api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fj.Equal; | |
import fj.Hash; | |
import fj.Ord; | |
import fj.Show; | |
import java.util.function.Function; | |
import org.derive4j.Data; | |
import org.derive4j.Derive; | |
import org.derive4j.Instances; | |
@Data(@Derive(@Instances({ Show.class, Hash.class, Equal.class, Ord.class}))) | |
public abstract class Either<A, B> { | |
/** | |
* The catamorphism for either. Folds over this either breaking into left or right. | |
* | |
* @param left The function to call if this is left. | |
* @param right The function to call if this is right. | |
* @return The reduced value. | |
*/ | |
public abstract <X> X either(Function<A, X> left, Function<B, X> right); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated from above | |
public final class Eithers { | |
//... | |
// Gnenerated from above (not very optimized yet): | |
public static <A, B> Show<Either<A, B>> eitherShow(Show<A> aShow, Show<B> bShow) { | |
return Show.show(either -> either.either( | |
(left) -> Stream.fromString("left(").append(() -> aShow.show(left)).append(Stream.fromString(")")), | |
(right) -> Stream.fromString("right(").append(() -> bShow.show(right)).append(Stream.fromString(")")) | |
)); | |
} | |
public static <A, B> Ord<Either<A, B>> eitherOrd(Ord<A> aOrd, Ord<B> bOrd) { | |
return Ord.ordDef(either1 -> either1.either( | |
(left1) -> either2 -> either2.either( | |
(left2) -> { | |
Ordering o = Ordering.EQ; | |
o = aOrd.compare(left1, left2); | |
if (o != Ordering.EQ) return o; | |
return o; | |
}, | |
(right2) -> Ordering.LT | |
), | |
(right1) -> either2 -> either2.either( | |
(left2) -> Ordering.GT, | |
(right2) -> { | |
Ordering o = Ordering.EQ; | |
o = bOrd.compare(right1, right2); | |
if (o != Ordering.EQ) return o; | |
return o; | |
} | |
) | |
)); | |
} | |
public static <A, B> Equal<Either<A, B>> eitherEqual(Equal<A> aEqual, Equal<B> bEqual) { | |
return Equal.equalDef(either1 -> either1.either( | |
(left1) -> either2 -> either2.either( | |
(left2) -> aEqual.eq(left1, left2), | |
(right2) -> false | |
), | |
(right1) -> either2 -> either2.either( | |
(left2) -> false, | |
(right2) -> bEqual.eq(right1, right2) | |
) | |
)); | |
} | |
public static <A, B> Hash<Either<A, B>> eitherHash(Hash<A> aHash, Hash<B> bHash) { | |
return Hash.hash(either -> either.either( | |
(left) -> 23 + aHash.hash(left), | |
(right) -> 29 + bHash.hash(right) | |
)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2017, Jean-Baptiste Giraudeau <[email protected]> | |
* | |
* This file is part of "Derive4J - FunctionalJava derivators". | |
* | |
* "Derive4J - FunctionalJava derivators" is free software: you can redistribute it | |
* and/or modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation, either version 3 of the License, | |
* or (at your option) any later version. | |
* | |
* "Derive4J - FunctionalJava derivators" is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with "Derive4J - FunctionalJava derivators". If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
package org.derive4j.fj; | |
import com.google.auto.service.AutoService; | |
import com.squareup.javapoet.ClassName; | |
import com.squareup.javapoet.CodeBlock; | |
import java.util.List; | |
import java.util.stream.Collectors; | |
import java.util.stream.IntStream; | |
import org.derive4j.processor.api.DerivatorFactory; | |
import org.derive4j.processor.api.DerivatorSelection; | |
import org.derive4j.processor.api.DeriveUtils; | |
import static java.util.Arrays.asList; | |
import static java.util.Collections.emptyList; | |
import static org.derive4j.processor.api.DerivatorSelections.selection; | |
@AutoService(DerivatorFactory.class) | |
public final class FunctionalJavaTypeClassesDerivations implements DerivatorFactory { | |
private static final List<Integer> PRIMES = asList(23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, | |
107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, | |
241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, | |
397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, | |
563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, | |
719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, | |
883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997); | |
@Override | |
public List<DerivatorSelection> derivators(DeriveUtils deriveUtils) { | |
final ClassName showClass = ClassName.get("fj", "Show"); | |
final ClassName hashClass = ClassName.get("fj", "Hash"); | |
final ClassName equalClass = ClassName.get("fj", "Equal"); | |
final ClassName ordClass = ClassName.get("fj", "Ord"); | |
final ClassName ordering = ClassName.get("fj", "Ordering"); | |
final ClassName streamClass = ClassName.get("fj.data", "Stream"); | |
return asList( | |
selection(showClass, adt -> deriveUtils.generateInstance(adt, showClass, emptyList(), instanceUtils -> | |
instanceUtils.generateInstanceFactory(CodeBlock.builder() | |
.add("$1T.show($2L -> $2L.", showClass, instanceUtils.adtVariableName()) | |
.add(instanceUtils.matchImpl(constructor -> deriveUtils.lambdaImpl(constructor, CodeBlock.builder() | |
.add("$T.fromString($S).append(() -> ", streamClass, constructor.name() + "(") | |
.add(constructor.arguments() | |
.stream() | |
.map(da -> instanceUtils.instanceFor(da).toBuilder().add(".show($L)", da.fieldName()).build()) | |
.reduce((cb1, cb2) -> cb1.toBuilder() | |
.add(".append($T.fromString($S)).append(() -> ", streamClass, ", ") | |
.add(cb2) | |
.add(")") | |
.build()) | |
.orElse(CodeBlock.of("$T.nil()", streamClass))) | |
.add(").append($T.fromString($S))", streamClass, ")") | |
.build()))) | |
.add(")") | |
.build()) | |
)), | |
selection(hashClass, adt -> deriveUtils.generateInstance(adt, hashClass, emptyList(), instanceUtils -> | |
instanceUtils.generateInstanceFactory(CodeBlock.builder() | |
.add("$1T.hash($2L -> $2L.", hashClass, instanceUtils.adtVariableName()) | |
.add(instanceUtils.matchImpl(constructor -> { | |
int nbConstructors = adt.dataConstruction().constructors().size(); | |
int constructorIndex = IntStream.range(0, nbConstructors) | |
.filter(i -> adt.dataConstruction().constructors().get(i).name().equals(constructor.name())) | |
.findFirst() | |
.getAsInt(); | |
return deriveUtils.lambdaImpl(constructor, CodeBlock.builder() | |
.add("$L", IntStream.range(0, constructor.arguments().size() - 1) | |
.mapToObj(__ -> "(") | |
.collect(Collectors.joining())) | |
.add(PRIMES.get(constructorIndex).toString()) | |
.add(constructor.arguments() | |
.stream() | |
.map(da -> CodeBlock.builder() | |
.add(" + ") | |
.add(instanceUtils.instanceFor(da)) | |
.add(".hash($L)", da.fieldName()) | |
.build()) | |
.reduce((cb1, cb2) -> cb1.toBuilder().add(") * " + PRIMES.get(constructorIndex)).add(cb2).build()) | |
.orElse(CodeBlock.of(""))) | |
.build()); | |
})) | |
.add(")") | |
.build()) | |
)), | |
selection(equalClass, adt -> deriveUtils.generateInstance(adt, equalClass, emptyList(), instanceUtils -> | |
instanceUtils.generateInstanceFactory(CodeBlock.builder() | |
.add("$1T.equalDef($2L -> $2L.", equalClass, instanceUtils.adtVariableName() + 1) | |
.add(instanceUtils.matchImpl(constructor -> { | |
String adt2 = instanceUtils.adtVariableName() + 2; | |
return deriveUtils.lambdaImpl(constructor, "1", CodeBlock.builder() | |
.add("$1L -> $1L.", adt2) | |
.add(instanceUtils.matchImpl(constructor2 -> deriveUtils.lambdaImpl(constructor2, "2", | |
constructor.name().equals(constructor2.name()) | |
? constructor.arguments() | |
.stream() | |
.map(da -> CodeBlock.builder() | |
.add(instanceUtils.instanceFor(da)) | |
.add(".eq($L, $L)", da.fieldName() + "1", da.fieldName() + "2") | |
.build()) | |
.reduce((cb1, cb2) -> cb1.toBuilder().add(" && ").add(cb2).build()) | |
.orElse(CodeBlock.of("true")) | |
: CodeBlock.of("false")))) | |
.build()); | |
})) | |
.add(")") | |
.build()) | |
)), | |
selection(ordClass, adt -> deriveUtils.generateInstance(adt, ordClass, emptyList(), instanceUtils -> | |
instanceUtils.generateInstanceFactory(CodeBlock.builder() | |
.add("$1T.ordDef($2L -> $2L.", ordClass, instanceUtils.adtVariableName() + 1) | |
.add(instanceUtils.matchImpl(constructor -> { | |
String adt2 = instanceUtils.adtVariableName() + 2; | |
return deriveUtils.lambdaImpl(constructor, "1", CodeBlock.builder() | |
.add("$1L -> $1L.", adt2) | |
.add(instanceUtils.matchImpl(constructor2 -> deriveUtils.lambdaImpl(constructor2, "2", | |
constructor.name().equals(constructor2.name()) | |
? CodeBlock.builder() | |
.add("{\n") | |
.indent() | |
.addStatement("$1T o = $1T.EQ", ordering) | |
.add(constructor.arguments() | |
.stream() | |
.map(da -> CodeBlock.builder() | |
.add("o = ") | |
.add(instanceUtils.instanceFor(da)) | |
.add(".compare($L, $L);\n", da.fieldName() + "1", da.fieldName() + "2") | |
.addStatement("if (o != $T.EQ) return o", ordering) | |
.build()) | |
.reduce((cb1, cb2) -> cb1.toBuilder().add(cb2).build()) | |
.orElse(CodeBlock.of(""))) | |
.addStatement("return o") | |
.unindent() | |
.add("}") | |
.build() | |
: CodeBlock.of("$T.$L", ordering, constructor.index() < constructor2.index() | |
? "LT" | |
: "GT")))) | |
.build()); | |
})) | |
.add(")") | |
.build()) | |
)) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment