From d83b38808f02f0d6839084771410d1fa4914376e Mon Sep 17 00:00:00 2001 From: Michael A Date: Wed, 23 Oct 2019 15:40:59 -0500 Subject: [PATCH 1/5] Upgrade Lambda, Traitor --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c13a1ce..face871 100644 --- a/pom.xml +++ b/pom.xml @@ -53,8 +53,8 @@ - 4.0.0 - 1.2 + 5.1.0 + 1.4.0 3.3 1.3 3.1.1 From 23435e68076dc287b68e3474e9be1034f4160946 Mon Sep 17 00:00:00 2001 From: Michael A Date: Mon, 9 Dec 2019 17:37:50 -0600 Subject: [PATCH 2/5] Added Coalgebra - issue #1 --- .../com/jnape/palatable/ouroboros/Coalgebra.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/ouroboros/Coalgebra.java diff --git a/src/main/java/com/jnape/palatable/ouroboros/Coalgebra.java b/src/main/java/com/jnape/palatable/ouroboros/Coalgebra.java new file mode 100644 index 0000000..a048e4a --- /dev/null +++ b/src/main/java/com/jnape/palatable/ouroboros/Coalgebra.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A {@link Coalgebra}<A, F> for some carrier type A and some {@link Functor} + * F is a morphism A -> F<A>. + * + * @param the carrier type + * @param the {@link Functor} witness + */ +@FunctionalInterface +public interface Coalgebra> extends Fn1 { +} From 9eb6f3b7ec3a4ef8ba01507bbc16d923658f6d98 Mon Sep 17 00:00:00 2001 From: Michael A Date: Mon, 16 Dec 2019 17:34:26 -0600 Subject: [PATCH 3/5] Anamorphisms --- .../palatable/ouroboros/Anamorphism.java | 65 +++++++++++++++++++ .../jnape/palatable/ouroboros/FixLazy.java | 39 +++++++++++ .../palatable/ouroboros/LazyAnamorphism.java | 58 +++++++++++++++++ .../palatable/ouroboros/AnamorphismTest.java | 36 ++++++++++ .../ouroboros/LazyAnamorphismTest.java | 18 +++++ 5 files changed, 216 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java create mode 100644 src/main/java/com/jnape/palatable/ouroboros/FixLazy.java create mode 100644 src/main/java/com/jnape/palatable/ouroboros/LazyAnamorphism.java create mode 100644 src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java create mode 100644 src/test/java/com/jnape/palatable/ouroboros/LazyAnamorphismTest.java diff --git a/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java b/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java new file mode 100644 index 0000000..3e7223f --- /dev/null +++ b/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java @@ -0,0 +1,65 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functor.Functor; +import com.jnape.palatable.lambda.functor.builtin.Lazy; +import com.jnape.palatable.lambda.monad.transformer.MonadT; +import com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; +import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.maybeT; +import static com.jnape.palatable.ouroboros.Fix.fix; + +/** + * An {@link Anamorphism} uses a {@link Coalgebra} to build up structure: + *
+ * {@code
+ * Coalgebra> generateToSize = x -> x < 3 ? just(x + 1) : nothing();
+ * ana(generateToSize, 0).unfix(); // Just fix(Just fix(Just fix(Nothing)))
+ * }
+ * 
+ * + * @param
the carrier type + * @param the {@link Functor} witness + * @param the {@link Functor} F<{@link Lazy}<A>> + */ +public final class Anamorphism, + FA extends Functor> + implements Fn2, A, Fix, F>>> { + private static final Anamorphism INSTANCE = new Anamorphism<>(); + + private Anamorphism() { + } + + @Override + public Fix, F>> checkedApply(Coalgebra coalgebra, A a) throws Throwable { + return fix(coalgebra.apply(a).fmap(ana(coalgebra))); + } + + @SuppressWarnings("unchecked") + public static , + FA extends Functor> Anamorphism ana() { + return (Anamorphism) INSTANCE; + } + + public static , + FA extends Functor> Fn1, F>>> ana(Coalgebra coalgebra) { + return Anamorphism.ana().apply(coalgebra); + } + + public static , + FA extends Functor> Fix, F>> ana(Coalgebra coalgebra, + A a) { + return ana(coalgebra).apply(a); + } +} diff --git a/src/main/java/com/jnape/palatable/ouroboros/FixLazy.java b/src/main/java/com/jnape/palatable/ouroboros/FixLazy.java new file mode 100644 index 0000000..378a619 --- /dev/null +++ b/src/main/java/com/jnape/palatable/ouroboros/FixLazy.java @@ -0,0 +1,39 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.functions.Fn0; +import com.jnape.palatable.lambda.functor.Functor; +import com.jnape.palatable.lambda.functor.builtin.Lazy; + +import java.util.Objects; + +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; + +public interface FixLazy, Unfixed extends Functor, F>> { + + Lazy unfixLazy(); + + static , Unfixed extends Functor, F>> FixLazy fixLazy( + Fn0 unfixed) { + return new FixLazy() { + @Override + public Lazy unfixLazy() { + return lazy(unfixed); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof FixLazy) && Objects.equals(unfixed, ((FixLazy) obj).unfixLazy()); + } + + @Override + public int hashCode() { + return 31 * Objects.hashCode(unfixed); + } + + @Override + public String toString() { + return "fix(" + unfixed + ")"; + } + }; + } +} diff --git a/src/main/java/com/jnape/palatable/ouroboros/LazyAnamorphism.java b/src/main/java/com/jnape/palatable/ouroboros/LazyAnamorphism.java new file mode 100644 index 0000000..7c38930 --- /dev/null +++ b/src/main/java/com/jnape/palatable/ouroboros/LazyAnamorphism.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.builtin.fn2.LazyRec; +import com.jnape.palatable.lambda.functor.Functor; +import com.jnape.palatable.lambda.functor.builtin.Lazy; + +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; + +/** + * A LazyAnamorphism builds up structure using a {@link Coalgebra}. Unlike an {@link Anamorphism}, a LazyAnamorphism may + * recurse infinitely. + * + * @param the carrier type + * @param the {@link Functor} witness + * @param the {@link Functor} F<{@link Lazy}<A>> + */ +public class LazyAnamorphism, + FA extends Functor> + implements Fn2, A, FixLazy, F>>> { + private static final LazyAnamorphism INSTANCE = new LazyAnamorphism<>(); + + private LazyAnamorphism() { + } + + @Override + public FixLazy, F>> checkedApply(Coalgebra coalgebra, A a) throws Throwable { + return LazyRec., F>>>lazyRec( + (f, anotherA) -> lazy(() -> FixLazy., F>>fixLazy(() -> coalgebra.apply(anotherA) + .fmap(f) + .fmap(Lazy::value))), + a).value(); + } + + @SuppressWarnings("unchecked") + public static , + FA extends Functor> + LazyAnamorphism lazyAna() { + return (LazyAnamorphism) INSTANCE; + } + + public static , + FA extends Functor> + Fn1, F>>> lazyAna(Coalgebra coalgebra) { + return LazyAnamorphism.lazyAna().apply(coalgebra); + } + + public static , + FA extends Functor> + FixLazy, F>> lazyAna(Coalgebra coalgebra, A a) { + return lazyAna(coalgebra).apply(a); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java b/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java new file mode 100644 index 0000000..10508e6 --- /dev/null +++ b/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java @@ -0,0 +1,36 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Functor; +import com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.maybeT; +import static com.jnape.palatable.ouroboros.Anamorphism.ana; +import static com.jnape.palatable.ouroboros.Fix.fix; +import static org.junit.Assert.assertEquals; + +public class AnamorphismTest { + + @Test + public void fromMaybe() { + Coalgebra> generateToSize = x -> x < 3 ? just(x + 1) : nothing(); + Functor, ?>, Maybe> unfix = ana(generateToSize, 0).unfix(); + + assertEquals(unfix, just(fix(just(fix(just(fix(nothing()))))))); + } + + @Test + public void collatzFromTuple() { + Coalgebra, Integer>> coalgebra = x -> x == 1 ? maybeT(tuple(x, nothing())) + : x % 2 == 0 ? maybeT(tuple(x, just(x / 2))) : maybeT(tuple(x, just(3 * x + 1))); + + assertEquals(ana(coalgebra, 4).unfix(), + maybeT(tuple(4, just(fix(maybeT(tuple(2, just(fix(maybeT(tuple(1, nothing()))))))))))); + } + +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/ouroboros/LazyAnamorphismTest.java b/src/test/java/com/jnape/palatable/ouroboros/LazyAnamorphismTest.java new file mode 100644 index 0000000..8de7594 --- /dev/null +++ b/src/test/java/com/jnape/palatable/ouroboros/LazyAnamorphismTest.java @@ -0,0 +1,18 @@ +package com.jnape.palatable.ouroboros; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Functor; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.ouroboros.LazyAnamorphism.lazyAna; + +public class LazyAnamorphismTest { + @Test + public void doesntStackOverflow() { + Coalgebra> coalgebra = x -> x % 2 == 0 ? tuple(x, x / 2) : tuple(x, 3 * x + 1); + + FixLazy, ? extends Functor, ?>, Tuple2>> ana = lazyAna(coalgebra, 15); + ana.unfixLazy(); + } +} \ No newline at end of file From 7ca5b2c403321cc1e8cb6c7bae739596346a4b8e Mon Sep 17 00:00:00 2001 From: Michael A Date: Mon, 16 Dec 2019 17:44:02 -0600 Subject: [PATCH 4/5] Fix broken Java 8 builds --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8ea4e57..39b2896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: java +dist: trusty jdk: - oraclejdk8 - oraclejdk11 From fa6f0f434539c4de876f7c54041ee4b360bb69e8 Mon Sep 17 00:00:00 2001 From: Michael A Date: Tue, 17 Dec 2019 07:28:38 -0600 Subject: [PATCH 5/5] Additional test for relation between ana & cata --- .../palatable/ouroboros/Anamorphism.java | 9 ------ .../palatable/ouroboros/AnamorphismTest.java | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java b/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java index 3e7223f..635dac0 100644 --- a/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java +++ b/src/main/java/com/jnape/palatable/ouroboros/Anamorphism.java @@ -1,19 +1,10 @@ package com.jnape.palatable.ouroboros; -import com.jnape.palatable.lambda.adt.Maybe; -import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.Fn2; import com.jnape.palatable.lambda.functor.Functor; import com.jnape.palatable.lambda.functor.builtin.Lazy; -import com.jnape.palatable.lambda.monad.transformer.MonadT; -import com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT; -import static com.jnape.palatable.lambda.adt.Maybe.just; -import static com.jnape.palatable.lambda.adt.Maybe.nothing; -import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; -import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; -import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.maybeT; import static com.jnape.palatable.ouroboros.Fix.fix; /** diff --git a/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java b/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java index 10508e6..a92c26e 100644 --- a/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java +++ b/src/test/java/com/jnape/palatable/ouroboros/AnamorphismTest.java @@ -2,16 +2,28 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.builtin.fn2.Cons; import com.jnape.palatable.lambda.functor.Functor; +import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT; import org.junit.Test; +import java.util.ArrayList; + import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Head.head; +import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection; +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.maybeT; import static com.jnape.palatable.ouroboros.Anamorphism.ana; +import static com.jnape.palatable.ouroboros.Catamorphism.cata; import static com.jnape.palatable.ouroboros.Fix.fix; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; public class AnamorphismTest { @@ -33,4 +45,22 @@ public void collatzFromTuple() { maybeT(tuple(4, just(fix(maybeT(tuple(2, just(fix(maybeT(tuple(1, nothing()))))))))))); } + @Test + public void collatzToCata() { + Coalgebra>, MaybeT, Lazy>>> coalgebra = x -> { + Integer value = head(x.value()).orElse(1); + return value == 1 ? maybeT(tuple(value, nothing())) + : value % 2 == 0 ? maybeT(tuple(value, just(lazy(singletonList(value / 2))))) : maybeT(tuple(value, just(lazy(singletonList(3 * value + 1))))); + }; + + Algebra, Lazy>>, Lazy>> algebra = mtii -> { + Tuple2>>> run = mtii.run(); + return lazy(Cons.cons(run._1(), run._2().match(constantly(emptyList()), + Lazy::value))); + }; + + assertEquals(toCollection(ArrayList::new, cata(algebra, ana(coalgebra, lazy(singletonList(3)))).value()), + asList(3, 10, 5, 16, 8, 4, 2, 1)); + } + } \ No newline at end of file