From 687c83eb74f42a75879bd2bbed373f36a1c8ebb3 Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Sat, 29 Apr 2023 15:32:56 -0400 Subject: [PATCH 1/7] Intitial applicatives zip embedded values with embedded functions --- .../lambdakoans/AboutApplicatives.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java new file mode 100644 index 0000000..feed1ba --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -0,0 +1,48 @@ +package com.jnape.palatable.lambdakoans; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.traversable.LambdaIterable; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static testsupport.matchers.IterableMatcher.iterates; + +public class AboutApplicatives { + @Test + public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { + Fn1 inc = x -> x + 1; + + assertThat(just(9).zip(just(inc)), equalTo(just(10))); + assertThat(just(15).zip(just(inc)), equalTo(just(16))); + // Explicit types on the nothing help the compiler + assertThat(Maybe.nothing().zip(just(inc)), equalTo(nothing())); + + Fn1 getStringLength = String::length; + assertThat(right("Hello").zip(right(getStringLength)), equalTo(right(5))); + assertThat(right("World!").zip(right(getStringLength)), equalTo(right(6))); + // Explicit types on the nothing help the compiler + assertThat(Either.left("whoops").zip(right(getStringLength)), equalTo(left("whoops"))); + + // Moving on to LambdaIterables, where "zipping" is more obvious + LambdaIterable oneThroughThree = LambdaIterable.wrap(asList(1, 2, 3)); + Applicative, LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); + assertThat(oneThroughThree.zip(wrappedInc).unwrap(), iterates(2, 3, 4)); + + Fn1 dec = x -> x - 1; + LambdaIterable> twoFunctions = LambdaIterable.wrap(asList(inc, dec)); + assertThat(oneThroughThree.zip(twoFunctions).unwrap(), iterates(2, 0, 3, 1, 4, 2)); + + Fn1 times3 = x -> x * 3; + LambdaIterable> allFunctions = LambdaIterable.wrap(asList(inc, dec, times3)); + assertThat(oneThroughThree.zip(allFunctions).unwrap(), iterates(2, 0, 3, 3, 1, 6, 4, 2, 9)); + } +} \ No newline at end of file From 5390efba28144668ae3f35ae6c2ecad3bc4c0422 Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Sat, 29 Apr 2023 15:33:57 -0400 Subject: [PATCH 2/7] Lazy applicatives Co-authored-by: Alexander Bandukwala <7h3kk1d@gmail.com> --- .../lambdakoans/AboutApplicatives.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index feed1ba..7872c24 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -4,13 +4,20 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.traversable.LambdaIterable; import org.junit.Test; +import java.util.concurrent.atomic.AtomicInteger; + import static com.jnape.palatable.lambda.adt.Either.left; import static com.jnape.palatable.lambda.adt.Either.right; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.functions.Fn1.fn1; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -45,4 +52,40 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { LambdaIterable> allFunctions = LambdaIterable.wrap(asList(inc, dec, times3)); assertThat(oneThroughThree.zip(allFunctions).unwrap(), iterates(2, 0, 3, 3, 1, 6, 4, 2, 9)); } + + @Test + public void lazyApplicatives() { + // Zipping LambdaIterables is lazy because LambdaIterables are lazy + LambdaIterable infiniteOnes = LambdaIterable.wrap(repeat(1)); + Fn1 inc = x -> x + 1; + + LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); + LambdaIterable zippedOnes = infiniteOnes.zip(wrappedInc); + assertThat(take(3, zippedOnes.unwrap()), iterates(2, 2, 2)); + + // We might lazily get a mapping function... + AtomicInteger computed = new AtomicInteger(0); + + Fn1>> expensiveWayToGetMaybeToString = fn1((Integer n) -> { + for (int i = 0; i < n; i++) { + computed.incrementAndGet(); + } + return just(Object::toString); + }); + + Lazy>> lazyGetToString = lazy(() -> + expensiveWayToGetMaybeToString.apply(100_000_000)); + + // ...then apply it with lazyZip + Maybe nothing = nothing(); + Lazy> lazyNothingToString = nothing.lazyZip(lazyGetToString); + + assertThat(lazyNothingToString.value(), equalTo(nothing())); + assertThat(computed.get(), equalTo(0)); + + // zip, however, we've eagerly generated a mapping function + Maybe nothingToString = nothing.zip(expensiveWayToGetMaybeToString.apply(100_000)); + assertThat(nothingToString, equalTo(nothing())); + assertThat(computed.get(), equalTo(100_000)); + } } \ No newline at end of file From 1271ab78d81ddad17821c2065f797d704216c2c4 Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Tue, 2 May 2023 23:22:10 -0400 Subject: [PATCH 3/7] Function is applicative Co-authored-by: Alexander Bandukwala <7h3kk1d@gmail.com> --- .../lambdakoans/AboutApplicatives.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index 7872c24..7517ae4 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -2,7 +2,11 @@ import com.jnape.palatable.lambda.adt.Either; 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.Fn3; +import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2; +import com.jnape.palatable.lambda.functions.builtin.fn4.LiftA3; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.traversable.LambdaIterable; @@ -14,6 +18,7 @@ import static com.jnape.palatable.lambda.adt.Either.right; 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.Fn1.fn1; import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; @@ -88,4 +93,28 @@ public void lazyApplicatives() { assertThat(nothingToString, equalTo(nothing())); assertThat(computed.get(), equalTo(100_000)); } + + @Test + public void functionIsApplicative() { + Fn1 strLen = String::length; + Fn1 toUpper = String::toUpperCase; + + // Result of unary function calls are passed to mapping function as arguments + Fn1> lengthAndUppercase = LiftA2.liftA2(Tuple2::tuple, strLen, toUpper); + assertThat(lengthAndUppercase.apply("hello world"), equalTo(tuple(11, "HELLO WORLD"))); + + Fn1 mod3 = i -> i % 3; + Fn1 div3 = i -> i / 3; + + Fn1 showDivision = LiftA2.liftA2((divided, remainder) -> String.format("%d * 3 + %d", divided, remainder), div3, mod3); + assertThat(showDivision.apply(10), equalTo("3 * 3 + 1")); + + + Fn1 findStart = s -> s.indexOf('j'); + Fn1 findEnd = s -> s.indexOf(' '); + Fn3 cutString = String::substring; + + Fn1 transformAndCut = LiftA3.liftA3(cutString, toUpper, findStart, findEnd); + assertThat(transformAndCut.apply("hellojava world"), equalTo("JAVA")); + } } \ No newline at end of file From eaa5be323d4102be64ea3eccc8df00fa189e2a68 Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Tue, 2 May 2023 23:23:05 -0400 Subject: [PATCH 4/7] Parallel computation in applicatives Co-authored-by: Alexander Bandukwala <7h3kk1d@gmail.com> --- .../lambdakoans/AboutApplicatives.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index 7517ae4..aad83c7 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -9,9 +9,12 @@ import com.jnape.palatable.lambda.functions.builtin.fn4.LiftA3; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.builtin.Lazy; +import com.jnape.palatable.lambda.io.IO; import com.jnape.palatable.lambda.traversable.LambdaIterable; import org.junit.Test; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import static com.jnape.palatable.lambda.adt.Either.left; @@ -117,4 +120,37 @@ public void functionIsApplicative() { Fn1 transformAndCut = LiftA3.liftA3(cutString, toUpper, findStart, findEnd); assertThat(transformAndCut.apply("hellojava world"), equalTo("JAVA")); } + + @Test + public void applicativeRepresentsParallelism() throws ExecutionException, InterruptedException { + IO foo = IO.io(() -> { + Thread.sleep(2_000); + return 14; + }); + + IO bar = IO.io(() -> { + Thread.sleep(2_000); + return 28; + }); + + IO applicativeInIo = LiftA2.liftA2(Integer::sum, foo, bar); + + long singleThreadStart = System.currentTimeMillis(); + + applicativeInIo + .flatMap(result -> IO.io(() -> assertThat(result, equalTo(42)))) + .unsafePerformIO(); + + System.out.printf("Single thread execution took %d seconds%n", (System.currentTimeMillis() - singleThreadStart) / 1000); + + // If we have multiple threads available, function evaluation is done in parallel + long multipleThreadStart = System.currentTimeMillis(); + + applicativeInIo + .flatMap(result -> IO.io(() -> assertThat(result, equalTo(42)))) + .unsafePerformAsyncIO(Executors.newFixedThreadPool(2)) + .get(); + + System.out.printf("Single thread execution took %d seconds%n", (System.currentTimeMillis() - multipleThreadStart) / 1000); + } } \ No newline at end of file From 7e5d71821e0e300812245ad0a0357e1afd864547 Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Thu, 4 May 2023 21:42:56 -0400 Subject: [PATCH 5/7] Omit assertion values, cleanup --- .../lambdakoans/AboutApplicatives.java | 37 ++++++++++--------- .../palatable/lambdakoans/AboutFunctors.java | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index aad83c7..bb8477b 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -26,26 +26,27 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; +import static com.jnape.palatable.lambdakoans.Koans.__; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; public class AboutApplicatives { + @Test public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { Fn1 inc = x -> x + 1; assertThat(just(9).zip(just(inc)), equalTo(just(10))); - assertThat(just(15).zip(just(inc)), equalTo(just(16))); - // Explicit types on the nothing help the compiler - assertThat(Maybe.nothing().zip(just(inc)), equalTo(nothing())); + assertThat(just(15).zip(just(inc)), equalTo(just(__))); + // Explicit types help the compiler + assertThat(Maybe.nothing().zip(just(inc)), equalTo(__)); Fn1 getStringLength = String::length; - assertThat(right("Hello").zip(right(getStringLength)), equalTo(right(5))); - assertThat(right("World!").zip(right(getStringLength)), equalTo(right(6))); - // Explicit types on the nothing help the compiler - assertThat(Either.left("whoops").zip(right(getStringLength)), equalTo(left("whoops"))); + assertThat(right("Hello").zip(right(getStringLength)), equalTo(__)); + assertThat(right("World!").zip(right(getStringLength)), equalTo(__)); + assertThat(Either.left("whoops").zip(right(getStringLength)), equalTo(left(__))); // Moving on to LambdaIterables, where "zipping" is more obvious LambdaIterable oneThroughThree = LambdaIterable.wrap(asList(1, 2, 3)); @@ -58,7 +59,7 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { Fn1 times3 = x -> x * 3; LambdaIterable> allFunctions = LambdaIterable.wrap(asList(inc, dec, times3)); - assertThat(oneThroughThree.zip(allFunctions).unwrap(), iterates(2, 0, 3, 3, 1, 6, 4, 2, 9)); + assertThat(oneThroughThree.zip(allFunctions).unwrap(), iterates(__())); } @Test @@ -69,7 +70,7 @@ public void lazyApplicatives() { LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); LambdaIterable zippedOnes = infiniteOnes.zip(wrappedInc); - assertThat(take(3, zippedOnes.unwrap()), iterates(2, 2, 2)); + assertThat(take(3, zippedOnes.unwrap()), iterates(__())); // We might lazily get a mapping function... AtomicInteger computed = new AtomicInteger(0); @@ -88,13 +89,13 @@ public void lazyApplicatives() { Maybe nothing = nothing(); Lazy> lazyNothingToString = nothing.lazyZip(lazyGetToString); - assertThat(lazyNothingToString.value(), equalTo(nothing())); - assertThat(computed.get(), equalTo(0)); + assertThat(lazyNothingToString.value(), equalTo(__)); + assertThat(computed.get(), equalTo(__)); // zip, however, we've eagerly generated a mapping function Maybe nothingToString = nothing.zip(expensiveWayToGetMaybeToString.apply(100_000)); - assertThat(nothingToString, equalTo(nothing())); - assertThat(computed.get(), equalTo(100_000)); + assertThat(nothingToString, equalTo(__)); + assertThat(computed.get(), equalTo(__)); } @Test @@ -110,7 +111,7 @@ public void functionIsApplicative() { Fn1 div3 = i -> i / 3; Fn1 showDivision = LiftA2.liftA2((divided, remainder) -> String.format("%d * 3 + %d", divided, remainder), div3, mod3); - assertThat(showDivision.apply(10), equalTo("3 * 3 + 1")); + assertThat(showDivision.apply(10), equalTo(__)); Fn1 findStart = s -> s.indexOf('j'); @@ -118,7 +119,7 @@ public void functionIsApplicative() { Fn3 cutString = String::substring; Fn1 transformAndCut = LiftA3.liftA3(cutString, toUpper, findStart, findEnd); - assertThat(transformAndCut.apply("hellojava world"), equalTo("JAVA")); + assertThat(transformAndCut.apply("hellojava world"), equalTo(__)); } @Test @@ -138,7 +139,7 @@ public void applicativeRepresentsParallelism() throws ExecutionException, Interr long singleThreadStart = System.currentTimeMillis(); applicativeInIo - .flatMap(result -> IO.io(() -> assertThat(result, equalTo(42)))) + .flatMap(result -> IO.io(() -> assertThat(result, equalTo(__)))) .unsafePerformIO(); System.out.printf("Single thread execution took %d seconds%n", (System.currentTimeMillis() - singleThreadStart) / 1000); @@ -147,10 +148,10 @@ public void applicativeRepresentsParallelism() throws ExecutionException, Interr long multipleThreadStart = System.currentTimeMillis(); applicativeInIo - .flatMap(result -> IO.io(() -> assertThat(result, equalTo(42)))) + .flatMap(result -> IO.io(() -> assertThat(result, equalTo(__)))) .unsafePerformAsyncIO(Executors.newFixedThreadPool(2)) .get(); - System.out.printf("Single thread execution took %d seconds%n", (System.currentTimeMillis() - multipleThreadStart) / 1000); + System.out.printf("Multiple thread execution took %d seconds%n", (System.currentTimeMillis() - multipleThreadStart) / 1000); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutFunctors.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutFunctors.java index 304886e..4934b6f 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutFunctors.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutFunctors.java @@ -68,6 +68,6 @@ public void fn1sAreAlsoFunctorsOverTheirOutputType() { Fn1 inc = x -> x + 1; Fn1 lengthThenInc = length.fmap(inc); // fmap for Fn1 is just left-to-right composition! - assertThat(lengthThenInc.apply("13 characters"), __()); + assertThat(lengthThenInc.apply("13 characters"), equalTo(__)); } } From e681444be33fb214dab34d79d9183e37cec09ccc Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Sat, 13 May 2023 18:16:49 -0400 Subject: [PATCH 6/7] Various changes from feedback - reorder functionIsApplicative - add assertion - change type signature - add timeout for applicativeRepresentsParallelism - add comments --- .../lambdakoans/AboutApplicatives.java | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index bb8477b..060bee9 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -7,7 +7,6 @@ import com.jnape.palatable.lambda.functions.Fn3; import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2; import com.jnape.palatable.lambda.functions.builtin.fn4.LiftA3; -import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.io.IO; import com.jnape.palatable.lambda.traversable.LambdaIterable; @@ -40,6 +39,7 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { assertThat(just(9).zip(just(inc)), equalTo(just(10))); assertThat(just(15).zip(just(inc)), equalTo(just(__))); + assertThat(just(7).zip(nothing()), equalTo(__)); // Explicit types help the compiler assertThat(Maybe.nothing().zip(just(inc)), equalTo(__)); @@ -50,7 +50,7 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { // Moving on to LambdaIterables, where "zipping" is more obvious LambdaIterable oneThroughThree = LambdaIterable.wrap(asList(1, 2, 3)); - Applicative, LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); + LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); assertThat(oneThroughThree.zip(wrappedInc).unwrap(), iterates(2, 3, 4)); Fn1 dec = x -> x - 1; @@ -62,6 +62,30 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { assertThat(oneThroughThree.zip(allFunctions).unwrap(), iterates(__())); } + @Test + public void functionIsApplicative() { + Fn1 strLen = String::length; + Fn1 toUpper = String::toUpperCase; + + // Result of unary function calls are passed to mapping function as arguments + Fn1> lengthAndUppercase = LiftA2.liftA2(Tuple2::tuple, strLen, toUpper); + assertThat(lengthAndUppercase.apply("hello world"), equalTo(tuple(11, "HELLO WORLD"))); + + Fn1 mod3 = i -> i % 3; + Fn1 div3 = i -> i / 3; + + Fn1 showDivision = LiftA2.liftA2((divided, remainder) -> String.format("%d * 3 + %d", divided, remainder), div3, mod3); + assertThat(showDivision.apply(10), equalTo(__)); + + + Fn1 findStart = s -> s.indexOf('j'); + Fn1 findEnd = s -> s.indexOf(' '); + Fn3 cutString = String::substring; + + Fn1 transformAndCut = LiftA3.liftA3(cutString, toUpper, findStart, findEnd); + assertThat(transformAndCut.apply("hellojava world"), equalTo(__)); + } + @Test public void lazyApplicatives() { // Zipping LambdaIterables is lazy because LambdaIterables are lazy @@ -85,44 +109,21 @@ public void lazyApplicatives() { Lazy>> lazyGetToString = lazy(() -> expensiveWayToGetMaybeToString.apply(100_000_000)); - // ...then apply it with lazyZip + // ...then apply it with lazyZip. Maybe nothing = nothing(); + // Note: unlike LambdaIterables, the Maybe inside is not itself lazy Lazy> lazyNothingToString = nothing.lazyZip(lazyGetToString); assertThat(lazyNothingToString.value(), equalTo(__)); assertThat(computed.get(), equalTo(__)); - // zip, however, we've eagerly generated a mapping function + // zip, however, eagerly generates a mapping function Maybe nothingToString = nothing.zip(expensiveWayToGetMaybeToString.apply(100_000)); assertThat(nothingToString, equalTo(__)); assertThat(computed.get(), equalTo(__)); } - @Test - public void functionIsApplicative() { - Fn1 strLen = String::length; - Fn1 toUpper = String::toUpperCase; - - // Result of unary function calls are passed to mapping function as arguments - Fn1> lengthAndUppercase = LiftA2.liftA2(Tuple2::tuple, strLen, toUpper); - assertThat(lengthAndUppercase.apply("hello world"), equalTo(tuple(11, "HELLO WORLD"))); - - Fn1 mod3 = i -> i % 3; - Fn1 div3 = i -> i / 3; - - Fn1 showDivision = LiftA2.liftA2((divided, remainder) -> String.format("%d * 3 + %d", divided, remainder), div3, mod3); - assertThat(showDivision.apply(10), equalTo(__)); - - - Fn1 findStart = s -> s.indexOf('j'); - Fn1 findEnd = s -> s.indexOf(' '); - Fn3 cutString = String::substring; - - Fn1 transformAndCut = LiftA3.liftA3(cutString, toUpper, findStart, findEnd); - assertThat(transformAndCut.apply("hellojava world"), equalTo(__)); - } - - @Test + @Test(timeout = 6500) public void applicativeRepresentsParallelism() throws ExecutionException, InterruptedException { IO foo = IO.io(() -> { Thread.sleep(2_000); @@ -149,7 +150,8 @@ public void applicativeRepresentsParallelism() throws ExecutionException, Interr applicativeInIo .flatMap(result -> IO.io(() -> assertThat(result, equalTo(__)))) - .unsafePerformAsyncIO(Executors.newFixedThreadPool(2)) + // How many threads should we use? + .unsafePerformAsyncIO(Executors.newFixedThreadPool(__())) .get(); System.out.printf("Multiple thread execution took %d seconds%n", (System.currentTimeMillis() - multipleThreadStart) / 1000); From 5d1d6da6ea86833c9c96bd5b07835c541cfae51a Mon Sep 17 00:00:00 2001 From: Tom Rutten Date: Mon, 15 May 2023 22:00:37 -0400 Subject: [PATCH 7/7] Comment about Cartesian product vs pairwise --- .../com/jnape/palatable/lambdakoans/AboutApplicatives.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java index 060bee9..741c28f 100644 --- a/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java +++ b/src/test/java/com/jnape/palatable/lambdakoans/AboutApplicatives.java @@ -53,9 +53,11 @@ public void applicativesZipEmbeddedValuesWithEmbeddedFunctions() { LambdaIterable> wrappedInc = LambdaIterable.wrap(asList(inc)); assertThat(oneThroughThree.zip(wrappedInc).unwrap(), iterates(2, 3, 4)); + // Zipping a LambdaIterable computes the Cartesian product of values with functions + // rather than apply the functions to values pairwise Fn1 dec = x -> x - 1; LambdaIterable> twoFunctions = LambdaIterable.wrap(asList(inc, dec)); - assertThat(oneThroughThree.zip(twoFunctions).unwrap(), iterates(2, 0, 3, 1, 4, 2)); + assertThat(oneThroughThree.zip(twoFunctions).unwrap(), iterates(2, 0, 3, 1, 4, __())); Fn1 times3 = x -> x * 3; LambdaIterable> allFunctions = LambdaIterable.wrap(asList(inc, dec, times3));