Skip to content

Instantly share code, notes, and snippets.

@jnape
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jnape/785e614867dbd2b0d5d1 to your computer and use it in GitHub Desktop.
Save jnape/785e614867dbd2b0d5d1 to your computer and use it in GitHub Desktop.
Continuation-like iterable type
package com.jnape.palatable.lambda.applicative;
import com.jnape.palatable.lambda.functions.MonadicFunction;
import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id;
@FunctionalInterface
public interface BiFunctor<A, B> {
default <C> BiFunctor<C, B> biMapL(MonadicFunction<? super A, ? extends C> fn) {
return biMap(fn, id());
}
default <C> BiFunctor<A, C> biMapR(MonadicFunction<? super B, ? extends C> fn) {
return biMap(id(), fn);
}
<C, D> BiFunctor<C, D> biMap(MonadicFunction<? super A, ? extends C> f1,
MonadicFunction<? super B, ? extends D> f2);
}
package com.jnape.palatable.lambda.continuation;
import com.jnape.palatable.lambda.applicative.Functor;
import com.jnape.palatable.lambda.functions.MonadicFunction;
import com.jnape.palatable.lambda.tuples.Tuple2;
import java.util.Iterator;
import java.util.Optional;
import static com.jnape.palatable.lambda.continuation.Memo.memoize;
import static com.jnape.palatable.lambda.tuples.Tuple2.tuple;
@FunctionalInterface
public interface Continuation<A> extends Functor<A>, Iterable<A> {
Optional<Tuple2<A, Continuation<A>>> next();
default Continuation<A> then(Continuation<A> more) {
return () -> next()
.map(t -> Optional.of(tuple(t._1, t._2.then(more))))
.orElseGet(more::next);
}
default <B> Continuation<B> remap(
MonadicFunction<Tuple2<A, Continuation<A>>, Optional<Tuple2<B, Continuation<B>>>> fn) {
return () -> next().flatMap(fn::apply);
}
default Continuation<A> memoized() {
class Memoized<X> implements Continuation<X> {
private final Memo<Optional<Tuple2<X, Continuation<X>>>> memoizedContinuation;
private Memoized(Continuation<X> continuation) {
memoizedContinuation = memoize(() -> continuation.next().map(t -> tuple(t._1, t._2.memoized())));
}
@Override
public Optional<Tuple2<X, Continuation<X>>> next() {
return memoizedContinuation.get();
}
}
return this instanceof Memoized ? this : new Memoized<>(this);
}
@Override
default <B> Continuation<B> fmap(MonadicFunction<? super A, ? extends B> fn) {
return () -> next().map(t -> t.biMap(fn, as -> as.fmap(fn)));
}
default boolean isEmpty() {
return next().isPresent();
}
@Override
default Iterator<A> iterator() {
return ContinuationIterator.wrap(this);
}
}
package com.jnape.palatable.lambda.continuation;
import java.util.Iterator;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
public final class Continuations {
private Continuations() {
}
@SafeVarargs
public static <A> Continuation<A> continuing(A... as) {
return continuing(asList(as));
}
public static <A> Continuation<A> continuing(Stream<A> as) {
return continuing(as.iterator());
}
public static <A> Continuation<A> continuing(Iterable<A> as) {
return as instanceof Continuation ? (Continuation<A>) as : continuing(as.iterator());
}
public static <A> Continuation<A> continuing(Iterator<A> as) {
return new IteratorWrappingContinuation<>(as);
}
public static String toString(Continuation continuation) {
StringBuilder sb = new StringBuilder("{");
for (Object element : continuation) {
sb.append(element instanceof Continuation ? Continuations.toString((Continuation) element) : element).append(", ");
}
return sb.delete(sb.length() - 2, sb.length()).append("}").toString();
}
}
package com.jnape.palatable.lambda.functions;
import com.jnape.palatable.lambda.applicative.ProFunctor;
import com.jnape.palatable.lambda.tuples.Tuple2;
@FunctionalInterface
public interface DyadicFunction<A, B, C> extends MonadicFunction<A, MonadicFunction<B, C>>, ProFunctor<B, C> {
C apply(A a, B b);
@Override
default MonadicFunction<B, C> apply(A a) {
return (b) -> apply(a, b);
}
@Override
default <C1> DyadicFunction<A, C1, C> diMapL(MonadicFunction<? super C1, ? extends B> fn) {
return (DyadicFunction<A, C1, C>) ProFunctor.super.diMapL(fn);
}
@Override
default <C1> DyadicFunction<A, B, C1> diMapR(MonadicFunction<? super C, ? extends C1> fn) {
return (DyadicFunction<A, B, C1>) ProFunctor.super.diMapR(fn);
}
@Override
default <D, E> DyadicFunction<A, D, E> diMap(MonadicFunction<? super D, ? extends B> f1,
MonadicFunction<? super C, ? extends E> f2) {
return (a, d) -> f2.apply(apply(a, f1.apply(d)));
}
default DyadicFunction<B, A, C> flip() {
return (b, a) -> apply(a, b);
}
default MonadicFunction<Tuple2<A, B>, C> uncurry() {
return (ab) -> apply(ab._1, ab._2);
}
}
package com.jnape.palatable.lambda.applicative;
import com.jnape.palatable.lambda.functions.MonadicFunction;
@FunctionalInterface
public interface Functor<A> {
<B> Functor<B> fmap(MonadicFunction<? super A, ? extends B> fn);
}
package com.jnape.palatable.lambda.functions;
import com.jnape.palatable.lambda.applicative.Functor;
@FunctionalInterface
public interface MonadicFunction<A, B> extends Functor<B> {
B apply(A a);
@Override
default <C> MonadicFunction<A, C> fmap(MonadicFunction<? super B, ? extends C> f) {
return a -> f.apply(apply(a));
}
default <C> MonadicFunction<A, C> then(MonadicFunction<? super B, ? extends C> f) {
return fmap(f);
}
default <Z> MonadicFunction<Z, B> compose(MonadicFunction<? super Z, ? extends A> g) {
return z -> apply(g.apply(z));
}
}
package com.jnape.palatable.lambda.applicative;
import com.jnape.palatable.lambda.functions.MonadicFunction;
import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id;
@FunctionalInterface
public interface ProFunctor<A, B> {
default <C> ProFunctor<C, B> diMapL(MonadicFunction<? super C, ? extends A> fn) {
return diMap(fn, id());
}
default <C> ProFunctor<A, C> diMapR(MonadicFunction<? super B, ? extends C> fn) {
return diMap(id(), fn);
}
<C, D> ProFunctor<C, D> diMap(MonadicFunction<? super C, ? extends A> f1,
MonadicFunction<? super B, ? extends D> f2);
}
package com.jnape.palatable.lambda.tuples;
import com.jnape.palatable.lambda.applicative.BiFunctor;
import com.jnape.palatable.lambda.applicative.Functor;
import com.jnape.palatable.lambda.functions.MonadicFunction;
import static java.lang.String.format;
public class Tuple2<_1, _2> implements Functor<_2>, BiFunctor<_1, _2> {
public final _1 _1;
public final _2 _2;
public Tuple2(_1 _1, _2 _2) {
this._1 = _1;
this._2 = _2;
}
@Override
public final <__2> Tuple2<_1, __2> fmap(MonadicFunction<? super _2, ? extends __2> fn) {
return biMapR(fn);
}
@Override
public final <C, D> Tuple2<C, D> biMap(MonadicFunction<? super _1, ? extends C> f1,
MonadicFunction<? super _2, ? extends D> f2) {
return tuple(f1.apply(_1), f2.apply(_2));
}
@Override
public final <C> Tuple2<C, _2> biMapL(MonadicFunction<? super _1, ? extends C> fn) {
return (Tuple2<C, _2>) BiFunctor.super.biMapL(fn);
}
@Override
public final <C> Tuple2<_1, C> biMapR(MonadicFunction<? super _2, ? extends C> fn) {
return (Tuple2<_1, C>) BiFunctor.super.biMapR(fn);
}
@Override
public String toString() {
return format("(%s, %s)", _1, _2);
}
@Override
public boolean equals(Object other) {
if (other instanceof Tuple2) {
Tuple2 that = (Tuple2) other;
return this._1.equals(that._1) && this._2.equals(that._2);
}
return false;
}
@Override
public int hashCode() {
int result = _1.hashCode();
return 31 * result + _2.hashCode();
}
public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) {
return new Tuple2<>(_1, _2);
}
}
@jnape
Copy link
Author

jnape commented May 11, 2015

remap makes things particularly interesting as a mechanism by which to lazily transform (in some cases, radically) the result of a continuation. Even complex folds can be implemented easily in terms of a remap.

Examples:

public static <A> Continuation<A> take(int n, Continuation<A> as) {
  return n > 0
    ? as.remap(t -> Optional.of(t.biMapR(take(n - 1))))
    : Optional::empty;
}
public static <A> Continuation<A> filter(MonadicFunction<? super A, Boolean> pred, Continuation<A> as) {
  return dropWhile(x -> !pred.apply(x), as)
    .remap(t -> Optional.of(t.biMapR(filter(pred))));
}
public static <A, B, C> Continuation<C> zipWith(
  DyadicFunction<? super A, ? super B, ? extends C> zipper, 
  Continuation<A> as, 
  Continuation<B> bs
) {
  return as.remap(
    ta -> bs.remap(
      tb -> Optional.of(tb.biMap(
        zipper.apply(ta._1),
        apply(zipper, ta._2)
      ))
    ).next());
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment