Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lambda Hacknight Changes #122

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jnape.palatable.lambda.functions.specialized;

import com.jnape.palatable.lambda.functions.Fn3;
import com.jnape.palatable.lambda.internal.Runtime;
import com.jnape.palatable.lambda.semigroup.Semigroup;

@FunctionalInterface
Expand All @@ -11,7 +12,11 @@ public interface SemigroupFactory<A, B> extends Fn3<A, B, B, B> {

@Override
default Semigroup<B> apply(A a) {
return Fn3.super.apply(a)::apply;
try {
return checkedApply(a);
} catch (Throwable t) {
throw Runtime.throwChecked(t);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.jnape.palatable.lambda.semigroup;

import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
import com.jnape.palatable.lambda.functor.builtin.Lazy;

import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;

public interface ShortCircuitingSemigroup<A> extends Semigroup<A> {
RecursiveResult<A, A> shortCircuitApply(A a1, A a2);

@Override
default A checkedApply(A a, A a2) throws Throwable {
return shortCircuitApply(a, a2).match(id(), id());
}

@Override
default A foldLeft(A a, Iterable<A> as) {
return trampoline(
into((acc, it) -> !it.hasNext() ? terminate(acc) : shortCircuitApply(acc, it.next())
.<RecursiveResult<A, A>>match(RecursiveResult::recurse,
RecursiveResult::terminate)
.biMapL(a3 -> tuple(a3, it))),
tuple(a, as.iterator()));
}

@Override
default Lazy<A> foldRight(A a, Iterable<A> as) {
return Semigroup.super.foldRight(a, as);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight;
import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2;
import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory;
import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.monoid.builtin.Present;
import com.jnape.palatable.lambda.semigroup.Semigroup;
import com.jnape.palatable.lambda.semigroup.ShortCircuitingSemigroup;

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.fn2.Into.into;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;

/**
Expand Down Expand Up @@ -63,19 +65,17 @@ public static <A> Maybe<A> absent(Semigroup<A> semigroup, Maybe<A> x, Maybe<A> y
}

private static <A> Semigroup<Maybe<A>> shortCircuitSemigroup(Semigroup<A> aSemigroup) {
return new Semigroup<Maybe<A>>() {
return new ShortCircuitingSemigroup<Maybe<A>>() {
@Override
public Maybe<A> checkedApply(Maybe<A> maybeX, Maybe<A> maybeY) {
return liftA2(aSemigroup, maybeX, maybeY);
public RecursiveResult<Maybe<A>, Maybe<A>> shortCircuitApply(Maybe<A> a1, Maybe<A> a2) {
return LiftA2.<A, A, A, Maybe<?>, Maybe<A>>liftA2(aSemigroup, a1, a2)
.match(constantly(terminate(nothing())),
a -> recurse(just(a)));
}

@Override
public Maybe<A> foldLeft(Maybe<A> acc, Iterable<Maybe<A>> maybes) {
return trampoline(
into((res, it) -> res.equals(nothing())
? terminate(res)
: recurse(tuple(liftA2(aSemigroup, res, it.next()), it))),
tuple(acc, maybes.iterator()));
public Maybe<A> checkedApply(Maybe<A> maybeX, Maybe<A> maybeY) {
return liftA2(aSemigroup, maybeX, maybeY);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.specialized.BiSemigroupFactory;
import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory;
import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.internal.Runtime;
import com.jnape.palatable.lambda.monoid.Monoid;
import com.jnape.palatable.lambda.semigroup.Semigroup;

import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map;
import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2;

/**
* A {@link Semigroup} instance formed by a <code>{@link Tuple2}&lt;_1, _2&gt;</code> and semigroups over
* <code>_1</code> and <code>_2</code>. Successively collapses multiple {@link Tuple2}s into a single {@link Tuple2} by
Expand All @@ -26,12 +32,6 @@ public final class Collapse<_1, _2> implements BiSemigroupFactory<Semigroup<_1>,
private Collapse() {
}

@Override
public Semigroup<Tuple2<_1, _2>> checkedApply(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) {
return (x, y) -> x.biMap(_1Semigroup.flip().apply(y._1()),
_2Semigroup.flip().apply(y._2()));
}

@SuppressWarnings("unchecked")
public static <_1, _2> Collapse<_1, _2> collapse() {
return (Collapse<_1, _2>) INSTANCE;
Expand All @@ -56,4 +56,38 @@ public static <_1, _2> Tuple2<_1, _2> collapse(Semigroup<_1> _1Semigroup, Semigr
Tuple2<_1, _2> y) {
return collapse(_1Semigroup, _2Semigroup, x).apply(y);
}

@Override
public Semigroup<Tuple2<_1, _2>> checkedApply(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) {
return new Semigroup<Tuple2<_1, _2>>() {
@Override
public Tuple2<_1, _2> checkedApply(Tuple2<_1, _2> x, Tuple2<_1, _2> y) {
return x.biMap(_1Semigroup.flip().apply(y._1()),
_2Semigroup.flip().apply(y._2()));
}

@Override
public Tuple2<_1, _2> foldLeft(Tuple2<_1, _2> tuple2, Iterable<Tuple2<_1, _2>> tuple2s) {
return tuple(_1Semigroup.foldLeft(tuple2._1(), map(Tuple2::_1, tuple2s)),
_2Semigroup.foldLeft(tuple2._2(), map(Tuple2::_2, tuple2s)));
}

@Override
public Lazy<Tuple2<_1, _2>> foldRight(Tuple2<_1, _2> tuple2, Iterable<Tuple2<_1, _2>> tuple2s) {
return liftA2(Tuple2::tuple,
_1Semigroup.foldRight(tuple2._1(), map(Tuple2::_1, tuple2s)),
_2Semigroup.foldRight(tuple2._2(), map(Tuple2::_2, tuple2s)));
}
};
}

@Override
public Semigroup<Tuple2<_1, _2>> apply(Semigroup<_1> semigroup, Semigroup<_2> semigroup2) {
try {
return checkedApply(semigroup, semigroup2);
} catch (Throwable t) {
throw Runtime.throwChecked(t);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@

import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.builtin.fn1.Distinct;
import com.jnape.palatable.lambda.semigroup.Semigroup;
import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
import com.jnape.palatable.lambda.semigroup.ShortCircuitingSemigroup;

import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Distinct.distinct;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Empty.empty;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
import static java.util.Collections.emptyList;

/**
* Given two {@link Iterable Iterables} <code>xs</code> and <code>ys</code>, return the {@link Distinct distinct}
* elements of <code>xs</code> that are also in <code>ys</code> in order of their unique occurrence in <code>xs</code>.
*
* @param <A> the {@link Iterable} element type
*/
public final class Intersection<A> implements Semigroup<Iterable<A>> {
public final class Intersection<A> implements ShortCircuitingSemigroup<Iterable<A>> {

private static final Intersection<?> INSTANCE = new Intersection<>();

private Intersection() {
}

@Override
public Iterable<A> checkedApply(Iterable<A> xs, Iterable<A> ys) {
return filter(x -> find(eq(x), ys).fmap(constantly(true)).orElse(false), distinct(xs));
}

@SuppressWarnings("unchecked")
public static <A> Intersection<A> intersection() {
return (Intersection<A>) INSTANCE;
Expand All @@ -40,4 +40,16 @@ public static <A> Fn1<Iterable<A>, Iterable<A>> intersection(Iterable<A> xs) {
public static <A> Iterable<A> intersection(Iterable<A> xs, Iterable<A> ys) {
return intersection(xs).apply(ys);
}

@Override
public RecursiveResult<Iterable<A>, Iterable<A>> shortCircuitApply(Iterable<A> a1, Iterable<A> a2) {
if (empty(a1) || empty(a2))
return terminate(emptyList());
return recurse(filter(x -> find(eq(x), a1).fmap(constantly(true)).orElse(false), distinct(a2)));
}

@Override
public Iterable<A> checkedApply(Iterable<A> xs, Iterable<A> ys) {
return filter(x -> find(eq(x), ys).fmap(constantly(true)).orElse(false), distinct(xs));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public void foldLeftShortCircuit() {
result = Absent.<Unit>absent(Constantly::constantly)
.foldLeft(nothing(), repeat(just(UNIT)));
assertEquals(nothing(), result);

result = Absent.<Unit>absent()
.apply(Constantly::constantly)
.foldLeft(nothing(), repeat(just(UNIT)));

assertEquals(nothing(), result);
}

@Test(timeout = 200)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,63 @@
package com.jnape.palatable.lambda.semigroup.builtin;

import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.adt.Unit;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.builtin.fn1.Constantly;
import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.semigroup.Semigroup;
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.Unit.UNIT;
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Zip.zip;
import static com.jnape.palatable.lambda.monoid.builtin.And.and;
import static com.jnape.palatable.lambda.monoid.builtin.Join.join;
import static com.jnape.palatable.lambda.monoid.builtin.Or.or;
import static com.jnape.palatable.lambda.semigroup.builtin.Absent.absent;
import static com.jnape.palatable.lambda.semigroup.builtin.Collapse.collapse;
import static org.junit.Assert.assertEquals;

public class CollapseTest {

@Test
public void semigroup() {
Semigroup<String> join = join();
Semigroup<Integer> add = Integer::sum;
Semigroup<String> join = join();
Semigroup<Integer> add = Integer::sum;

Collapse<String, Integer> collapse = collapse();
assertEquals(tuple("foobar", 3), collapse.apply(join, add, tuple("foo", 1), tuple("bar", 2)));
}

@Test(timeout = 200)
public void foldLeftShortCircuits() {
Semigroup<Tuple2<Boolean, Boolean>> collapse = collapse(and(), or());
Iterable<Tuple2<Boolean, Boolean>> tuples = zip(cons(false, repeat(true)),
cons(true, repeat(false)));
Tuple2<Boolean, Boolean> booleanBooleanTuple2 = collapse.foldLeft(tuple(true, false),
tuples);
assertEquals(tuple(false, true),
booleanBooleanTuple2);
}

@Test(timeout = 200)
public void foldRightShortCircuits() {
Semigroup<Maybe<Unit>> absent = absent(Constantly::constantly);
Semigroup<Tuple2<Maybe<Unit>, Maybe<Unit>>> collapse = collapse(absent, absent);

Lazy<Tuple2<Maybe<Unit>, Maybe<Unit>>> maybeLazy = collapse
.foldRight(tuple(just(UNIT), just(UNIT)),
repeat(tuple(nothing(), nothing())));
Tuple2<Maybe<Unit>, Maybe<Unit>> result = maybeLazy.value();
assertEquals(tuple(nothing(), nothing()), result);

result = collapse
.foldRight(tuple(nothing(), nothing()),
repeat(tuple(just(UNIT), just(UNIT)))).value();
assertEquals(tuple(nothing(), nothing()), result);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jnape.palatable.lambda.semigroup.builtin;

import com.jnape.palatable.lambda.adt.Unit;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.traitor.annotations.TestTraits;
import com.jnape.palatable.traitor.runners.Traits;
Expand All @@ -10,6 +11,8 @@
import testsupport.traits.InfiniteIterableSupport;
import testsupport.traits.Laziness;

import static com.jnape.palatable.lambda.adt.Unit.UNIT;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
import static com.jnape.palatable.lambda.semigroup.builtin.Intersection.intersection;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
Expand Down Expand Up @@ -40,4 +43,10 @@ public void intersectionIsCommonElementsAcrossIterables() {
assertThat(intersection(singletonList(1), singletonList(2)), isEmpty());
assertThat(intersection(asList(1, 2, 3, 3), singletonList(3)), iterates(3));
}

@Test(timeout = 200)
public void foldLeftShortCircuits() {
assertThat(Intersection.<Unit>intersection().foldLeft(emptyList(), repeat(singletonList(UNIT))), isEmpty());
assertThat(Intersection.<Unit>intersection().foldLeft(singletonList(UNIT), repeat(emptyList())), isEmpty());
}
}