Skip to content

Commit 5d7ff9e

Browse files
committed
Continuations arrive
Out-of-the-box support for Iterable/Iterator/Stream wrapping and memoization. Major overhaul around BIFs using Continuation instead of Iterable on the horizon. General housekeeping: - Making Identity function a singleton - Adding missing tests around (bi/di)map(L/R) for BiFunctor and ProFunctor, respectively, as well as canonical implementations Tuple2 and DyadicFunction, respectively - Adding missing tests around Tuple2 and Tuple3
1 parent 72f5fd5 commit 5d7ff9e

23 files changed

Lines changed: 595 additions & 5 deletions

src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id;
66

7+
@FunctionalInterface
78
public interface BiFunctor<A, B> {
89

910
default <C> BiFunctor<C, B> biMapL(MonadicFunction<? super A, ? extends C> fn) {

src/main/java/com/jnape/palatable/lambda/applicative/Functor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.jnape.palatable.lambda.functions.MonadicFunction;
44

5+
@FunctionalInterface
56
public interface Functor<A> {
67

78
<B> Functor<B> fmap(MonadicFunction<? super A, ? extends B> fn);

src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id;
66

7+
@FunctionalInterface
78
public interface ProFunctor<A, B> {
89

910
default <C> ProFunctor<C, B> diMapL(MonadicFunction<? super C, ? extends A> fn) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.jnape.palatable.lambda.continuation;
2+
3+
import com.jnape.palatable.lambda.applicative.Functor;
4+
import com.jnape.palatable.lambda.functions.MonadicFunction;
5+
import com.jnape.palatable.lambda.tuples.Tuple2;
6+
7+
import java.util.Iterator;
8+
import java.util.Optional;
9+
10+
import static com.jnape.palatable.lambda.continuation.Memo.memoize;
11+
import static com.jnape.palatable.lambda.tuples.Tuple2.tuple;
12+
13+
@FunctionalInterface
14+
public interface Continuation<A> extends Functor<A>, Iterable<A> {
15+
16+
Optional<Tuple2<A, Continuation<A>>> next();
17+
18+
@Override
19+
default Iterator<A> iterator() {
20+
return ContinuationIterator.wrap(this);
21+
}
22+
23+
default Continuation<A> then(Continuation<A> more) {
24+
return () -> next()
25+
.map(t -> Optional.of(tuple(t._1, t._2.then(more))))
26+
.orElseGet(more::next);
27+
}
28+
29+
@Override
30+
default <B> Continuation<B> fmap(MonadicFunction<? super A, ? extends B> fn) {
31+
return (() -> next().map(t -> t.biMap(fn, c -> c.fmap(fn))));
32+
}
33+
34+
default Continuation<A> memoized() {
35+
return memoize((() -> next().map(t -> tuple(t._1, t._2.memoized()))))::get;
36+
}
37+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.jnape.palatable.lambda.continuation;
2+
3+
import com.jnape.palatable.lambda.tuples.Tuple2;
4+
5+
import java.util.Iterator;
6+
import java.util.NoSuchElementException;
7+
8+
public final class ContinuationIterator<A> implements Iterator<A> {
9+
private Continuation<A> continuation;
10+
11+
public ContinuationIterator(Continuation<A> continuation) {
12+
this.continuation = continuation;
13+
}
14+
15+
@Override
16+
public boolean hasNext() {
17+
return continuation.next().isPresent();
18+
}
19+
20+
@Override
21+
public A next() {
22+
if (!hasNext())
23+
throw new NoSuchElementException();
24+
25+
Tuple2<A, Continuation<A>> continuationResult = continuation.next().get();
26+
continuation = continuationResult._2;
27+
return continuationResult._1;
28+
}
29+
30+
public static <A> ContinuationIterator<A> wrap(Continuation<A> continuable) {
31+
return new ContinuationIterator<>(continuable);
32+
}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.jnape.palatable.lambda.continuation;
2+
3+
import java.util.Iterator;
4+
import java.util.stream.Stream;
5+
6+
import static java.util.Arrays.asList;
7+
8+
public final class Continuations {
9+
10+
private Continuations() {
11+
}
12+
13+
@SafeVarargs
14+
public static <A> Continuation<A> continuing(A... as) {
15+
return continuing(asList(as));
16+
}
17+
18+
public static <A> Continuation<A> continuing(Stream<A> as) {
19+
return continuing(as.iterator());
20+
}
21+
22+
public static <A> Continuation<A> continuing(Iterable<A> as) {
23+
return continuing(as.iterator());
24+
}
25+
26+
public static <A> Continuation<A> continuing(Iterator<A> as) {
27+
return new IteratorWrappingContinuation<>(as);
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.jnape.palatable.lambda.continuation;
2+
3+
import com.jnape.palatable.lambda.tuples.Tuple2;
4+
5+
import java.util.Iterator;
6+
import java.util.Optional;
7+
8+
import static com.jnape.palatable.lambda.continuation.Memo.memoize;
9+
import static com.jnape.palatable.lambda.tuples.Tuple2.tuple;
10+
11+
public final class IteratorWrappingContinuation<A> implements Continuation<A> {
12+
private final Memo<Optional<Tuple2<A, Continuation<A>>>> memoizedResult;
13+
14+
public IteratorWrappingContinuation(Iterator<A> iterator) {
15+
memoizedResult = memoize(() -> (iterator.hasNext())
16+
? Optional.of(tuple(iterator.next(), new IteratorWrappingContinuation<>(iterator)))
17+
: Optional.empty());
18+
}
19+
20+
@Override
21+
public Optional<Tuple2<A, Continuation<A>>> next() {
22+
return memoizedResult.get();
23+
}
24+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.jnape.palatable.lambda.continuation;
2+
3+
import java.util.concurrent.atomic.AtomicReference;
4+
import java.util.function.Supplier;
5+
6+
public final class Memo<T> {
7+
private final AtomicReference<T> valueRef;
8+
private final Supplier<T> fn;
9+
10+
private Memo(Supplier<T> fn) {
11+
this.fn = fn;
12+
valueRef = new AtomicReference<>();
13+
}
14+
15+
public T get() {
16+
if (valueRef.get() == null)
17+
valueRef.compareAndSet(null, fn.get());
18+
return valueRef.get();
19+
}
20+
21+
public static <T> Memo<T> memoize(Supplier<T> fn) {
22+
return new Memo<>(fn);
23+
}
24+
25+
public static <T> Memo<T> memoize(T result) {
26+
return memoize(() -> result);
27+
}
28+
29+
30+
}

src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Identity.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
public final class Identity<A> implements MonadicFunction<A, A> {
66

7+
private static final Identity IDENTITY = new Identity();
8+
79
@Override
810
public final A apply(A a) {
911
return a;
1012
}
1113

14+
@SuppressWarnings("unchecked")
1215
public static <A> Identity<A> id() {
13-
return new Identity<>();
16+
return (Identity<A>) IDENTITY;
1417
}
1518
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.jnape.palatable.lambda.applicative;
2+
3+
import com.jnape.palatable.lambda.functions.MonadicFunction;
4+
import org.junit.Test;
5+
import testsupport.applicatives.InvocationRecordingBiFunctor;
6+
7+
import java.util.concurrent.atomic.AtomicReference;
8+
9+
import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id;
10+
import static org.hamcrest.CoreMatchers.is;
11+
import static org.junit.Assert.assertThat;
12+
13+
public class BiFunctorTest {
14+
15+
@Test
16+
public void biMapLUsesIdentityForRightBiMapFunction() {
17+
AtomicReference<MonadicFunction> rightInvocation = new AtomicReference<>();
18+
BiFunctor<String, Integer> biFunctor = new InvocationRecordingBiFunctor<>(new AtomicReference<>(), rightInvocation);
19+
biFunctor.biMapL(String::toUpperCase);
20+
assertThat(rightInvocation.get(), is(id()));
21+
}
22+
23+
@Test
24+
public void biMapRUsesIdentityForLeftBiMapFunction() {
25+
AtomicReference<MonadicFunction> leftInvocation = new AtomicReference<>();
26+
BiFunctor<String, Integer> biFunctor = new InvocationRecordingBiFunctor<>(leftInvocation, new AtomicReference<>());
27+
biFunctor.biMapR(String::valueOf);
28+
assertThat(leftInvocation.get(), is(id()));
29+
}
30+
}

0 commit comments

Comments
 (0)