diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index 3fcf1775..cdb8e746 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -17,6 +17,7 @@ import static fj.data.List.nil; import static fj.data.Natural.natural; import static fj.data.Option.none; +import static fj.data.Option.some; import static fj.data.Stream.iterableStream; import java.math.BigInteger; @@ -867,6 +868,36 @@ public List sum(F0>> as) { }); } + /** + * Lift a {@code Semigroup} for A to a {@code Monoid>}, using Option.none() as zero. + * + * @return A monoid for option. + */ + public static Monoid> optionMonoid(Semigroup aSemigroup) { + return monoidDef(new Monoid.Definition>() { + @Override + public Option empty() { + return none(); + } + + @Override + public Option append(Option a1, Option a2) { + return a1.liftM2(a2, aSemigroup::sum).orElse(a1).orElse(a2); + } + + @Override + public Option multiply(int n, Option oa) { + return n > 0 ? oa.map(a -> aSemigroup.multiply1p(n - 1, a)) : none(); + } + + @Override + public Option sum(F0>> oas) { + Stream as = oas.f().bind(Option::toStream); + return as.uncons(none(), h -> tail -> some(aSemigroup.sumStream(h, tail::_1))); + } + }); + } + /** * A monoid for options. * @deprecated since 4.7. Use {@link #firstOptionMonoid()}. diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 16b0d130..f626ded7 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -172,29 +172,7 @@ public Semigroup dual() { * Lifts the semigroup to obtain a trivial monoid. */ public Monoid> lift() { - Definition def = this.def; - return monoidDef(new Monoid.Definition>() { - @Override - public Option empty() { - return none(); - } - - @Override - public Option append(Option a1, Option a2) { - return a1.liftM2(a1, def::append).orElse(a1).orElse(a2); - } - - @Override - public Option multiply(int n, Option oa) { - return n > 0 ? oa.map(a -> def.multiply1p(n - 1, a)) : none(); - } - - @Override - public Option sum(F0>> oas) { - Stream as = oas.f().bind(Option::toStream); - return as.uncons(none(), h -> tail -> some(def.sum(h, tail::_1))); - } - }); + return Monoid.optionMonoid(this); } /** diff --git a/core/src/main/java/fj/control/Trampoline.java b/core/src/main/java/fj/control/Trampoline.java index 37491c3b..0b6060b1 100644 --- a/core/src/main/java/fj/control/Trampoline.java +++ b/core/src/main/java/fj/control/Trampoline.java @@ -46,7 +46,7 @@ public R fold(final F, R> n, // The monadic bind constructs a new Codense whose subcomputation is still `sub`, and Kleisli-composes the // continuations. public Trampoline bind(final F> f) { - return codense(sub, o -> suspend(P.lazy(() -> cont.f(o).bind(f)))); + return codense(sub, o -> suspend(() -> cont.f(o).bind(f))); } // The resumption of a Codense is the resumption of its subcomputation. If that computation is done, its result @@ -126,6 +126,16 @@ public static Trampoline pure(final A a) { return new Pure<>(a); } + /** + * Suspends the given computation in a thunk. + * + * @param a A trampoline suspended in a thunk. + * @return A trampoline whose next step runs the given thunk. + */ + public static Trampoline suspend(final F0> a) { + return new Suspend<>(P.lazy(a)); + } + /** * Suspends the given computation in a thunk. * @@ -136,6 +146,7 @@ public static Trampoline suspend(final P1> a) { return new Suspend<>(a); } + /** * @return The first-class version of `suspend`. */ @@ -255,7 +266,7 @@ public final Trampoline zipWith(final Trampoline b, final F2>, B> eb = b.resume(); for (final P1> x : ea.left()) { for (final P1> y : eb.left()) { - return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(P.lazy(() -> ta.zipWith(tb, f)))))); + return suspend(x.bind(y, F2Functions.curry((ta, tb) -> suspend(() -> ta.zipWith(tb, f))))); } for (final B y : eb.right()) { return suspend(x.map(ta -> ta.map(F2Functions.f(F2Functions.flip(f), y)))); @@ -263,7 +274,7 @@ public final Trampoline zipWith(final Trampoline b, final F2 pure(f.f(x, y)))); + return suspend(() -> pure(f.f(x, y))); } for (final P1> y : eb.left()) { return suspend(y.map(liftM2(F2Functions.curry(f)).f(pure(x)))); diff --git a/core/src/main/java/fj/data/DList.java b/core/src/main/java/fj/data/DList.java index af9d68b0..597b6dda 100644 --- a/core/src/main/java/fj/data/DList.java +++ b/core/src/main/java/fj/data/DList.java @@ -126,6 +126,6 @@ public DList append(DList other) { } private static F> kleisliTrampCompose(F> bc, F> ab) { - return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(P.lazy(() -> bc.f(b)))); + return (A a) -> ab.f(a).bind((B b) -> Trampoline.suspend(() -> bc.f(b))); } } diff --git a/core/src/main/java/fj/data/Eval.java b/core/src/main/java/fj/data/Eval.java index 3e513075..5d09f913 100644 --- a/core/src/main/java/fj/data/Eval.java +++ b/core/src/main/java/fj/data/Eval.java @@ -204,7 +204,7 @@ private static final class PureTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> Trampoline.pure(start.value()))); + return Trampoline.suspend(() -> Trampoline.pure(start.value())); } } @@ -219,7 +219,7 @@ private static final class BindTrampolineEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline()))); + return Trampoline.suspend(() -> next.trampoline().bind(v -> f.f(v).asTrampoline().trampoline())); } } @@ -232,7 +232,7 @@ private static final class DeferEval extends TrampolineEval { @Override protected final Trampoline trampoline() { - return Trampoline.suspend(P.lazy(() -> memo._1().asTrampoline().trampoline())); + return Trampoline.suspend(() -> memo._1().asTrampoline().trampoline()); } } } diff --git a/core/src/main/java/fj/data/List.java b/core/src/main/java/fj/data/List.java index f58fd73f..e0e12877 100644 --- a/core/src/main/java/fj/data/List.java +++ b/core/src/main/java/fj/data/List.java @@ -736,7 +736,7 @@ public final B foldRight(final F2 f, final B b) { * @return A Trampoline containing the final result after the right-fold reduction. */ public final Trampoline foldRightC(final F2 f, final B b) { - return Trampoline.suspend(P.lazy(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head())))); + return Trampoline.suspend(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head()))); } /** diff --git a/core/src/main/java/fj/data/State.java b/core/src/main/java/fj/data/State.java index 1b30a55e..f29d55d7 100644 --- a/core/src/main/java/fj/data/State.java +++ b/core/src/main/java/fj/data/State.java @@ -5,7 +5,6 @@ import fj.Unit; import fj.control.Trampoline; -import static fj.P.lazy; import static fj.P.p; import static fj.control.Trampoline.suspend; import static fj.data.List.cons; @@ -75,7 +74,7 @@ public static State> traverse(List list, F State suspended(F>> runF) { - return new State<>(s -> suspend(lazy(() -> runF.f(s)))); + return new State<>(s -> suspend(() -> runF.f(s))); } private final F>> runF; diff --git a/core/src/test/java/fj/MonoidTest.java b/core/src/test/java/fj/MonoidTest.java new file mode 100644 index 00000000..8bab5315 --- /dev/null +++ b/core/src/test/java/fj/MonoidTest.java @@ -0,0 +1,19 @@ +package fj; + +import fj.data.Option; +import fj.data.Stream; +import org.junit.Test; + +import static fj.data.Option.some; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class MonoidTest { + + @Test + public void lifted_sum_of_two_numbers() { + Monoid> optionMonoid = Semigroup.intAdditionSemigroup.lift(); + assertThat(optionMonoid.sum(some(3), some(5)), is(some(8))); + assertThat(optionMonoid.sumLeft(Stream.arrayStream(some(3), some(5))), is(some(8))); + } +} diff --git a/quickcheck/src/main/java/fj/test/Gen.java b/quickcheck/src/main/java/fj/test/Gen.java index a5e30a9d..323995e1 100644 --- a/quickcheck/src/main/java/fj/test/Gen.java +++ b/quickcheck/src/main/java/fj/test/Gen.java @@ -16,7 +16,6 @@ import static fj.Function.flip; import static fj.Monoid.intAdditionMonoid; import static fj.Ord.intOrd; -import static fj.P.lazy; import static fj.P2.__1; import static fj.control.Trampoline.pure; import static fj.control.Trampoline.suspend; @@ -551,7 +550,7 @@ final class Tramp { // Picks elements in constant stack space private Trampoline> tramp(List remainAs, int remainN, int remainALength) { - return suspend(lazy(() -> + return suspend(() -> (remainN == 0) ? // We have picked N elements; stop pure(nil()) : @@ -559,7 +558,7 @@ private Trampoline> tramp(List remainAs, int remainN, int remainALeng (r.choose(0, remainALength - 1) < remainN) ? tramp(remainAs.tail(), remainN - 1, remainALength - 1) .map(pickedTail -> cons(remainAs.head(), pickedTail)) : - tramp(remainAs.tail(), remainN, remainALength - 1))); + tramp(remainAs.tail(), remainN, remainALength - 1)); } }