From 3b053fed3bd58fa20be233715ba1212e0ef1c6ef Mon Sep 17 00:00:00 2001 From: Daniel Gronau Date: Tue, 19 Dec 2017 17:35:54 +0100 Subject: [PATCH 1/2] #76 invective and isomorphic type classes - initial commit --- .../highj/typeclass2/injective/Injective.java | 29 +++++++++++ .../typeclass2/injective/Injective1.java | 39 +++++++++++++++ .../typeclass2/injective/Isomorphic.java | 35 +++++++++++++ .../typeclass2/injective/Isomorphic1.java | 50 +++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 src/main/java/org/highj/typeclass2/injective/Injective.java create mode 100644 src/main/java/org/highj/typeclass2/injective/Injective1.java create mode 100644 src/main/java/org/highj/typeclass2/injective/Isomorphic.java create mode 100644 src/main/java/org/highj/typeclass2/injective/Isomorphic1.java diff --git a/src/main/java/org/highj/typeclass2/injective/Injective.java b/src/main/java/org/highj/typeclass2/injective/Injective.java new file mode 100644 index 0000000..345a8c9 --- /dev/null +++ b/src/main/java/org/highj/typeclass2/injective/Injective.java @@ -0,0 +1,29 @@ +package org.highj.typeclass2.injective; + +import java.util.function.Function; + +/** + * An injective relationship. + * + * Law: + * if x != y then injective.to(x) != injective.to(y) + * + * @param source type + * @param target type + */ +public interface Injective extends Function { + + B to(A input); + + default B apply(A input) { + return to(input); + } + + static Injective of(Function f) { + return f::apply; + } + + static Injective identity() { + return a -> a; + } +} diff --git a/src/main/java/org/highj/typeclass2/injective/Injective1.java b/src/main/java/org/highj/typeclass2/injective/Injective1.java new file mode 100644 index 0000000..8682daf --- /dev/null +++ b/src/main/java/org/highj/typeclass2/injective/Injective1.java @@ -0,0 +1,39 @@ +package org.highj.typeclass2.injective; + +import org.derive4j.hkt.__; + +/** + * An injective relationship of type constructors. + * + * Law: + * if x != y then injective1.to(x) != injective1.to(y) + * + * @param the source type constructor + * @param the target type constructor + */ +public interface Injective1 { + + __ to(__ input); + + default Injective<__, __> to() { + return this::to; + } + + default Injective1 andThen(Injective1 that) { + return new Injective1() { + @Override + public __ to(__ input) { + return that.to(Injective1.this.to(input)); + } + }; + } + + static Injective1 identity() { + return new Injective1() { + @Override + public __ to(__ input) { + return input; + } + }; + } +} diff --git a/src/main/java/org/highj/typeclass2/injective/Isomorphic.java b/src/main/java/org/highj/typeclass2/injective/Isomorphic.java new file mode 100644 index 0000000..d62fe9d --- /dev/null +++ b/src/main/java/org/highj/typeclass2/injective/Isomorphic.java @@ -0,0 +1,35 @@ +package org.highj.typeclass2.injective; + +import java.util.function.Function; + +/** + * The class of isomorphic types, i.e. those which can be cast to each other without loss of information. + * @param source type + * @param target type + */ +public interface Isomorphic extends Injective { + + A from(B input); + + static Isomorphic of(Function fnTo, Function fnFrom) { + return new Isomorphic() { + @Override + public A from(B input) { + return fnFrom.apply(input); + } + + @Override + public B to(A input) { + return fnTo.apply(input); + } + }; + } + + static Isomorphic identity() { + return Isomorphic.of(a -> a, a -> a); + } + + default Isomorphic inverse() { + return Isomorphic.of(Isomorphic.this::from, Isomorphic.this::to); + } +} diff --git a/src/main/java/org/highj/typeclass2/injective/Isomorphic1.java b/src/main/java/org/highj/typeclass2/injective/Isomorphic1.java new file mode 100644 index 0000000..5944b98 --- /dev/null +++ b/src/main/java/org/highj/typeclass2/injective/Isomorphic1.java @@ -0,0 +1,50 @@ +package org.highj.typeclass2.injective; + +import org.derive4j.hkt.__; + +/** + * The class of isomorphic type constructors, i.e. those which can be cast to each other without loss of information. + * @param + * @param + */ +public interface Isomorphic1 extends Injective1 { + + __ from(__ input); + + default Injective<__, __> from() { + return this::from; + } + + default Isomorphic<__, __> isomorphic() { + return Isomorphic.of(f -> Isomorphic1.this.to(f), g -> Isomorphic1.this.from(g)); + } + + default Isomorphic1 inverse() { + return new Isomorphic1() { + @Override + public __ from(__ input) { + return Isomorphic1.this.to(input); + } + + @Override + public __ to(__ input) { + return Isomorphic1.this.from(input); + } + }; + } + + static Isomorphic1 identity() { + return new Isomorphic1() { + @Override + public __ from(__ input) { + return input; + } + + @Override + public __ to(__ input) { + return input; + } + }; + } + +} From 6851719ae8940d7d4111279153e65a8901bac8b8 Mon Sep 17 00:00:00 2001 From: DanielGronau Date: Wed, 20 Dec 2017 00:00:15 +0100 Subject: [PATCH 2/2] injective and isomorphic laws, and an Isomorphic1 example --- .../maybe/MaybeEitherIsomorphic1.java | 20 ++++++++++ .../typeclass2/injective/InjectiveLaw.java | 37 ++++++++++++++++++ .../typeclass2/injective/IsomorphicLaw.java | 39 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/main/java/org/highj/data/instance/maybe/MaybeEitherIsomorphic1.java create mode 100644 src/test/java/org/highj/typeclass2/injective/InjectiveLaw.java create mode 100644 src/test/java/org/highj/typeclass2/injective/IsomorphicLaw.java diff --git a/src/main/java/org/highj/data/instance/maybe/MaybeEitherIsomorphic1.java b/src/main/java/org/highj/data/instance/maybe/MaybeEitherIsomorphic1.java new file mode 100644 index 0000000..d8ae1c4 --- /dev/null +++ b/src/main/java/org/highj/data/instance/maybe/MaybeEitherIsomorphic1.java @@ -0,0 +1,20 @@ +package org.highj.data.instance.maybe; + +import org.derive4j.hkt.__; +import org.highj.Hkt; +import org.highj.data.Either; +import org.highj.data.Maybe; +import org.highj.data.tuple.T0; +import org.highj.typeclass2.injective.Isomorphic1; + +public interface MaybeEitherIsomorphic1 extends Isomorphic1> { + @Override + default Maybe from(__<__, A> input) { + return Hkt.asEither(input).either(u -> Maybe.Nothing(), Maybe::Just); + } + + @Override + default Either to(__ input) { + return Hkt.asMaybe(input).cata(Either.Left(T0.unit), Either::Right); + } +} diff --git a/src/test/java/org/highj/typeclass2/injective/InjectiveLaw.java b/src/test/java/org/highj/typeclass2/injective/InjectiveLaw.java new file mode 100644 index 0000000..7271d53 --- /dev/null +++ b/src/test/java/org/highj/typeclass2/injective/InjectiveLaw.java @@ -0,0 +1,37 @@ +package org.highj.typeclass2.injective; + +import org.highj.data.eq.Eq; +import org.highj.util.Gen; +import org.highj.util.Law; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InjectiveLaw implements Law { + + private final Injective injective; + protected final Gen genA; + protected final Eq eqA; + protected final Eq eqB; + + public InjectiveLaw(Injective injective, Gen genA, Eq eqA, Eq eqB) { + this.injective = injective; + this.genA = genA; + this.eqA = eqA; + this.eqB = eqB; + } + + public void injectivity(){ + for(A a1 : genA.get(10)) { + B b1 = injective.to(a1); + for(A a2 : genA.get(10)) { + B b2 = injective.to(a2); + assertThat(eqA.eq(a1, a2) == eqB.eq(b1, b2)).isTrue(); + } + } + } + + @Override + public void test() { + injectivity(); + } +} diff --git a/src/test/java/org/highj/typeclass2/injective/IsomorphicLaw.java b/src/test/java/org/highj/typeclass2/injective/IsomorphicLaw.java new file mode 100644 index 0000000..d68cd3c --- /dev/null +++ b/src/test/java/org/highj/typeclass2/injective/IsomorphicLaw.java @@ -0,0 +1,39 @@ +package org.highj.typeclass2.injective; + +import org.highj.data.eq.Eq; +import org.highj.util.Gen; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IsomorphicLaw extends InjectiveLaw { + + private final Isomorphic isomorphic; + protected final Gen genB; + + public IsomorphicLaw(Isomorphic isomorphic, Gen genA, Gen genB, Eq eqA, Eq eqB) { + super(isomorphic, genA, eqA, eqB); + this.isomorphic = isomorphic; + this.genB = genB; + } + + public void isomorphism() { + for(A a : genA.get(20)) { + assertThat(eqA.eq(a, isomorphic.from(isomorphic.to(a)))).isTrue(); + } + for(B b : genB.get(20)) { + assertThat(eqB.eq(b, isomorphic.to(isomorphic.from(b)))).isTrue(); + } + } + + public void inverseInjectivity() { + new InjectiveLaw<>(isomorphic.inverse(), genB, eqB, eqA).test(); + } + + @Override + public void test() { + isomorphism(); + inverseInjectivity(); + super.test(); + } + +}