diff --git a/README.md b/README.md index 9d819fb8f..076a31dad 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,26 @@ -lambda +λ ====== [![Build Status](https://travis-ci.org/palatable/lambda.svg)](https://travis-ci.org/palatable/lambda) Functional patterns for Java 8 +Background +---------- + +Lambda was born out of a desire to use some of the same canonical functions (e.g. `unfoldr`, `takeWhile`, `zipWith`) and functional patterns (e.g. `Functor` and friends) that are idiomatic in other languages and make them available for Java. + +Some things a user of lambda most likely values: + +- Lazy evaluation +- Immutablility by design +- Composition +- Higher-level abstractions +- Parametric polymorphism + +Generally, everything that lambda produces is lazily-evaluated (except for terminal operations like `reduce`), immutable (except for `Iterator`s, since it's effectively impossible), composable (even between different arities, where possible), foundational (maximally contravariant), and parametrically type-checked (even where this adds unnecessary constraints due to a lack of higher-kinded types). + +Although the library is currently (very) small, these values should always be the driving forces behind future growth. + Installation ------------ @@ -15,32 +32,16 @@ Add the following dependency to your: com.jnape.palatable lambda - 1.0 + 1.1 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle - compile group: 'com.jnape.palatable', name: 'lambda', version: '1.0' + compile group: 'com.jnape.palatable', name: 'lambda', version: '1.1' ``` -Background ----------- - -Lambda was born out of a desire to use some of the same canonical functions (e.g. `unfoldr`, `takeWhile`, `zipWith`) and functional patterns (e.g. `Functor` and friends) that are idiomatic in other languages and make them available for Java. - -Some things a user of lambda most likely values: - -- Lazy evaluation -- Immutablility by design -- Composition -- Higher-level abstractions -- Parametric polymorphism - -Generally, everything that lambda produces is lazily-evaluated (except for terminal operations like `reduce`), immutable (except for `Iterator`s, since it's effectively impossible), composable (even between different arities, where possible), foundational (maximally contravariant), and parametrically type-checked (even where this adds unnecessary constraints due to a lack of higher-kinded types). - -Although the library is currently (very) small, these values should always be the driving forces behind future growth. Examples -------- @@ -125,42 +126,65 @@ Or check out [the tests](https://github.com/palatable/lambda/tree/master/src/tes ADTs ---- -In addition to the functions above, Lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type). +In addition to the functions above, lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type). -### Tuples +### Heterogeneous Lists (HLists) -There are two parametric variants on the tuple product type, `Tuple2<_1, _2>` and `Tuple3<_1, _2, _3>`, both immutable. +HLists are type-safe heterogeneous lists, meaning they can store elements of different types in the same list while facilitating certain type-safe interactions. + +The following illustrates how the linear expansion of the recursive type signature for `HList` prevents ill-typed expressions: ```Java - Tuple2 tuple2 = Tuple2.tuple("foo", 1); + HCons> hList = HList.cons(1, HList.cons("foo", HList.nil())); + + System.out.println(hList.head()); // prints 1 + System.out.println(hList.tail().head()); // prints "foo" - System.out.println(tuple2._1); // prints "foo" - System.out.println(tuple2._2); // prints 1 + HNil nil = hList.tail().tail(); + //nil.head() won't type-check ``` -```Java - Tuple3 tuple3 = Tuple3.tuple("foo", true, 1); +#### Tuples - System.out.println(tuple3._1); // prints "foo" - System.out.println(tuple3._2); // prints true - System.out.println(tuple3._3); // prints 1 -``` +One of the primary downsides to using `HList`s in Java is how quickly the type signature grows. -Both `Tuple2` and `Tuple3` are `BiFunctor`s over `_1` and `_2`: +To address this, tuples in lambda are specializations of `HList`s up to 5 elements deep, with added support for index-based accessor methods. ```Java - Tuple2 mappedTuple2 = tuple2.biMap(String::toUpperCase, x -> x + 1); + HNil nil = HList.nil(); + Singleton singleton = nil.cons(5); + Tuple2 tuple2 = singleton.cons(4); + Tuple3 tuple3 = tuple2.cons(3); + Tuple4 tuple4 = tuple3.cons(2); + Tuple5 tuple5 = tuple4.cons(1); + + System.out.println(tuple2._1()); // prints 4 + System.out.println(tuple5._5()); // prints 5 +``` - System.out.println(mappedTuple2._1); // prints "FOO" - System.out.println(mappedTuple2._2); // prints 2 +Additionally, `HList` provides convenience static factory methods for directly constructing lists of up to 5 elements: + +```Java + Singleton singleton = HList.singleton(1); + Tuple2 tuple2 = HList.tuple(1, 2); + Tuple3 tuple3 = HList.tuple(1, 2, 3); + Tuple4 tuple4 = HList.tuple(1, 2, 3, 4); + Tuple5 tuple5 = HList.tuple(1, 2, 3, 4, 5); ``` +Finally, all `Tuple*` classes are instances of both `Functor` and `Bifunctor`: + ```Java - Tuple3 mappedTuple3 = tuple3.biMap(String::toUpperCase, b -> !b); + Tuple2 mappedTuple2 = tuple(1, 2).biMap(x -> x + 1, Object::toString); + + System.out.println(mappedTuple2._1()); // prints 2 + System.out.println(mappedTuple2._2()); // prints "2" + + Tuple3 mappedTuple3 = tuple("foo", true, 1).biMap(x -> !x, x -> x + 1); - System.out.println(mappedTuple3._1); // prints "FOO" - System.out.println(mappedTuple3._2); // prints false - System.out.println(mappedTuple3._3); // prints 1 + System.out.println(mappedTuple3._1()); // prints "foo" + System.out.println(mappedTuple3._2()); // prints false + System.out.println(mappedTuple3._3()); // prints 2 ``` ### Either diff --git a/pom.xml b/pom.xml index fac9872d1..30e571f91 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 1.1 + 1.2 jar Lambda diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java index 2bb5b6c5f..b286f7d54 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -8,6 +8,7 @@ import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Supplier; import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; @@ -22,6 +23,9 @@ */ public abstract class Either implements Functor, Bifunctor { + private Either() { + } + public final R or(R defaultValue) { return recover(l -> defaultValue); } @@ -63,6 +67,21 @@ public final Either merge(DyadicFunction other.match(Either::left, r2 -> right(rightFn.apply(r1, r2)))); } + public Either peek(Consumer rightConsumer) { + return peek(l -> { + }, rightConsumer); + } + + public Either peek(Consumer leftConsumer, Consumer rightConsumer) { + return flatMap(l -> { + leftConsumer.accept(l); + return this; + }, r -> { + rightConsumer.accept(r); + return this; + }); + } + public abstract V match(MonadicFunction leftFn, MonadicFunction rightFn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/HList.java deleted file mode 100644 index 13d6b4958..000000000 --- a/src/main/java/com/jnape/palatable/lambda/adt/HList.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.jnape.palatable.lambda.adt; - -import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.functor.Functor; - -/** - * A heterogeneous list supporting arbitrary depth type-safety via a linearly recursive type signature. Note that due to - * its rapidly expanding type signature, specializations exist up to certain depths to minimize typing overhead. - * - * @param The head element type - * @param The encoded recursive tail HList type - */ -public abstract class HList> { - - private HList() { - } - - public abstract HCons> cons(NewHead newHead); - - public static HNil nil() { - return HNil.INSTANCE; - } - - public static > HCons cons(Head head, Tail tail) { - return new HCons<>(head, tail); - } - - public static HCons1 list(Head head) { - return new HCons1<>(head); - } - - public static HCons2 list(H1 h1, H2 h2) { - return list(h2).cons(h1); - } - - public static HCons3 list(H1 h1, H2 h2, H3 h3) { - return list(h2, h3).cons(h1); - } - - public static HCons4 list(H1 h1, H2 h2, H3 h3, H4 h4) { - return list(h2, h3, h4).cons(h1); - } - - public static HCons5 list(H1 h1, H2 h2, H3 h3, H4 h4, H5 h5) { - return list(h2, h3, h4, h5).cons(h1); - } - - public static final class HCons5 extends HCons> { - private HCons5(A a, HCons4 tail) { - super(a, tail); - } - - @Override - public HCons> cons(NewHead newHead) { - return new HCons<>(newHead, this); - } - } - - public static final class HCons4 extends HCons> { - private HCons4(A a, HCons3 tail) { - super(a, tail); - } - - @Override - public HCons5 cons(NewHead newHead) { - return new HCons5<>(newHead, this); - } - } - - public static final class HCons3 extends HCons> { - private HCons3(A a, HCons2 tail) { - super(a, tail); - } - - @Override - public HCons4 cons(NewHead newHead) { - return new HCons4<>(newHead, this); - } - } - - public static final class HCons2 extends HCons> { - public HCons2(A a, HCons1 tail) { - super(a, tail); - } - - @Override - public HCons3 cons(NewHead newHead) { - return new HCons3<>(newHead, this); - } - } - - public static final class HCons1 extends HCons { - private HCons1(Head head) { - super(head, nil()); - } - - @Override - public HCons2 cons(NewHead newHead) { - return new HCons2<>(newHead, this); - } - } - - public static class HCons> extends HList implements Functor { - private final Head head; - private final Tail tail; - - private HCons(Head head, Tail tail) { - this.head = head; - this.tail = tail; - } - - public Head head() { - return head; - } - - public Tail tail() { - return tail; - } - - @Override - public HCons> cons(NewHead newHead) { - return new HCons<>(newHead, this); - } - - @Override - public HCons fmap(MonadicFunction fn) { - return new HCons<>(fn.apply(head), tail); - } - - @Override - public boolean equals(Object other) { - if (other instanceof HCons) { - HCons that = (HCons) other; - boolean sameHead = this.head.equals(that.head); - boolean sameTail = this.tail.equals(that.tail); - return sameHead && sameTail; - } - return false; - } - - @Override - public int hashCode() { - return 31 * head.hashCode() + tail.hashCode(); - } - - @Override - public String toString() { - return "HCons{" + - "head=" + head + - ", tail=" + tail + - '}'; - } - } - - public static final class HNil extends HList { - private static final HNil INSTANCE = new HNil(); - - @Override - public HCons cons(NewHead newHead) { - return new HCons<>(newHead, this); - } - - @Override - public String toString() { - return "HNil{}"; - } - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java new file mode 100644 index 000000000..feee8be92 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java @@ -0,0 +1,112 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import java.util.Objects; + +/** + * A heterogeneous list supporting arbitrary depth type-safety via a linearly recursive type signature. Note that due to + * its rapidly expanding type signature, specializations exist up to certain depths to minimize typing overhead. + * + * @param The head element type + * @param The encoded recursive tail HList type + * @see Singleton + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + */ +public abstract class HList> { + + private HList() { + } + + public abstract HCons> cons(NewHead newHead); + + public static HNil nil() { + return HNil.INSTANCE; + } + + public static > HCons cons(Head head, Tail tail) { + return new HCons<>(head, tail); + } + + public static Singleton singleton(Head head) { + return new Singleton<>(head); + } + + public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { + return singleton(_2).cons(_1); + } + + public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { + return tuple(_2, _3).cons(_1); + } + + public static <_1, _2, _3, _4> Tuple4<_1, _2, _3, _4> tuple(_1 _1, _2 _2, _3 _3, _4 _4) { + return tuple(_2, _3, _4).cons(_1); + } + + public static <_1, _2, _3, _4, _5> Tuple5<_1, _2, _3, _4, _5> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5) { + return tuple(_2, _3, _4, _5).cons(_1); + } + + public static class HCons> extends HList { + private final Head head; + private final Tail tail; + + HCons(Head head, Tail tail) { + this.head = head; + this.tail = tail; + } + + public Head head() { + return head; + } + + public Tail tail() { + return tail; + } + + @Override + public HCons> cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + + @Override + public final boolean equals(Object other) { + if (other instanceof HCons) { + HCons that = (HCons) other; + boolean sameHead = this.head.equals(that.head); + boolean sameTail = this.tail.equals(that.tail); + return sameHead && sameTail; + } + return false; + } + + @Override + public final int hashCode() { + return 31 * Objects.hashCode(head) + tail.hashCode(); + } + + @Override + public final String toString() { + return "HCons{" + + "head=" + head + + ", tail=" + tail + + '}'; + } + } + + public static final class HNil extends HList { + private static final HNil INSTANCE = new HNil(); + + @Override + public Singleton cons(Head head) { + return new Singleton<>(head); + } + + @Override + public String toString() { + return "HNil{}"; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Singleton.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Singleton.java new file mode 100644 index 000000000..fc6e66ee5 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Singleton.java @@ -0,0 +1,33 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.adt.hlist.HList.HNil; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A singleton HList. + * + * @param <_1> The single slot element type + * @see HList + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + */ +public final class Singleton<_1> extends HCons<_1, HNil> implements Functor<_1> { + + Singleton(_1 _1) { + super(_1, nil()); + } + + @Override + public <_0> Tuple2<_0, _1> cons(_0 _0) { + return new Tuple2<>(_0, this); + } + + @Override + public <_1Prime> Singleton<_1Prime> fmap(MonadicFunction fn) { + return new Singleton<>(fn.apply(head())); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java new file mode 100644 index 000000000..35001f291 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java @@ -0,0 +1,81 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; + +import java.util.Map; + +/** + * A 2-element tuple product type, implemented as a specialized HList. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @see HList + * @see Singleton + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + */ +public final class Tuple2<_1, _2> extends HCons<_1, Singleton<_2>> implements Map.Entry<_1, _2>, Functor<_2>, Bifunctor<_1, _2> { + + Tuple2(_1 _1, Singleton<_2> tail) { + super(_1, tail); + } + + @Override + public <_0> Tuple3<_0, _1, _2> cons(_0 _0) { + return new Tuple3<>(_0, this); + } + + public _1 _1() { + return head(); + } + + public _2 _2() { + return tail().head(); + } + + @Override + public _1 getKey() { + return _1(); + } + + @Override + public _2 getValue() { + return _2(); + } + + @Override + public _2 setValue(_2 value) { + throw new UnsupportedOperationException(); + } + + @Override + public <_2Prime> Tuple2<_1, _2Prime> fmap(MonadicFunction fn) { + return tuple(_1(), fn.apply(_2())); + } + + @Override + @SuppressWarnings("unchecked") + public <_1Prime> Tuple2<_1Prime, _2> biMapL(MonadicFunction fn) { + return (Tuple2<_1Prime, _2>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_2Prime> Tuple2<_1, _2Prime> biMapR(MonadicFunction fn) { + return (Tuple2<_1, _2Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_1Prime, _2Prime> Tuple2<_1Prime, _2Prime> biMap(MonadicFunction lFn, + MonadicFunction rFn) { + return new Tuple2<>(lFn.apply(_1()), tail().fmap(rFn)); + } + + public static Tuple2 fromEntry(Map.Entry entry) { + return new Tuple2<>(entry.getKey(), singleton(entry.getValue())); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java new file mode 100644 index 000000000..45c711b1d --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java @@ -0,0 +1,64 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A 3-element tuple product type, implemented as a specialized HList. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @see HList + * @see Singleton + * @see Tuple2 + * @see Tuple4 + * @see Tuple5 + */ +public final class Tuple3<_1, _2, _3> extends HCons<_1, Tuple2<_2, _3>> implements Functor<_3>, Bifunctor<_2, _3> { + Tuple3(_1 _1, Tuple2<_2, _3> tail) { + super(_1, tail); + } + + @Override + public <_0> Tuple4<_0, _1, _2, _3> cons(_0 _0) { + return new Tuple4<>(_0, this); + } + + public _1 _1() { + return head(); + } + + public _2 _2() { + return tail()._1(); + } + + public _3 _3() { + return tail()._2(); + } + + @Override + public <_3Prime> Tuple3<_1, _2, _3Prime> fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_2Prime> Tuple3<_1, _2Prime, _3> biMapL(MonadicFunction fn) { + return (Tuple3<_1, _2Prime, _3>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_3Prime> Tuple3<_1, _2, _3Prime> biMapR(MonadicFunction fn) { + return (Tuple3<_1, _2, _3Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_2Prime, _3Prime> Tuple3<_1, _2Prime, _3Prime> biMap(MonadicFunction lFn, + MonadicFunction rFn) { + return new Tuple3<>(_1(), tail().biMap(lFn, rFn)); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java new file mode 100644 index 000000000..1e655389f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java @@ -0,0 +1,69 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A 4-element tuple product type, implemented as a specialized HList. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @param <_4> The fourth slot element type + * @see HList + * @see Singleton + * @see Tuple2 + * @see Tuple3 + * @see Tuple5 + */ +public final class Tuple4<_1, _2, _3, _4> extends HCons<_1, Tuple3<_2, _3, _4>> implements Functor<_4>, Bifunctor<_3, _4> { + Tuple4(_1 _1, Tuple3<_2, _3, _4> tail) { + super(_1, tail); + } + + @Override + public <_0> Tuple5<_0, _1, _2, _3, _4> cons(_0 _0) { + return new Tuple5<>(_0, this); + } + + public _1 _1() { + return head(); + } + + public _2 _2() { + return tail()._1(); + } + + public _3 _3() { + return tail()._2(); + } + + public _4 _4() { + return tail()._3(); + } + + @Override + public <_4Prime> Tuple4<_1, _2, _3, _4Prime> fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_3Prime> Tuple4<_1, _2, _3Prime, _4> biMapL(MonadicFunction fn) { + return (Tuple4<_1, _2, _3Prime, _4>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_4Prime> Tuple4<_1, _2, _3, _4Prime> biMapR(MonadicFunction fn) { + return (Tuple4<_1, _2, _3, _4Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_3Prime, _4Prime> Tuple4<_1, _2, _3Prime, _4Prime> biMap(MonadicFunction lFn, + MonadicFunction rFn) { + return new Tuple4<>(_1(), tail().biMap(lFn, rFn)); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java new file mode 100644 index 000000000..ead0b387f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -0,0 +1,75 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A 5-element tuple product type, implemented as a specialized HList. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @param <_4> The fourth slot element type + * @param <_5> The fifth slot element type + * @see HList + * @see Singleton + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + */ +public final class Tuple5<_1, _2, _3, _4, _5> extends HCons<_1, Tuple4<_2, _3, _4, _5>> implements Functor<_5>, Bifunctor<_4, _5> { + Tuple5(_1 a, Tuple4<_2, _3, _4, _5> tail) { + super(a, tail); + } + + @Override + public <_0> HCons<_0, Tuple5<_1, _2, _3, _4, _5>> cons(_0 _0) { + return new HCons<>(_0, this); + } + + public _1 _1() { + return head(); + } + + public _2 _2() { + return tail()._1(); + } + + public _3 _3() { + return tail()._2(); + } + + public _4 _4() { + return tail()._3(); + } + + public _5 _5() { + return tail()._4(); + } + + @Override + public <_5Prime> Tuple5<_1, _2, _3, _4, _5Prime> fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_4Prime> Tuple5<_1, _2, _3, _4Prime, _5> biMapL(MonadicFunction fn) { + return (Tuple5<_1, _2, _3, _4Prime, _5>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_5Prime> Tuple5<_1, _2, _3, _4, _5Prime> biMapR(MonadicFunction fn) { + return (Tuple5<_1, _2, _3, _4, _5Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_4Prime, _5Prime> Tuple5<_1, _2, _3, _4Prime, _5Prime> biMap( + MonadicFunction lFn, + MonadicFunction rFn) { + return new Tuple5<>(_1(), tail().biMap(lFn, rFn)); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java new file mode 100644 index 000000000..537f61dc5 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java @@ -0,0 +1,112 @@ +package com.jnape.palatable.lambda.adt.hmap; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +import static com.jnape.palatable.lambda.functions.builtin.dyadic.Map.map; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; + +/** + * A heterogeneous mapping from a parametrized type-safe key to any value, supporting a minimal mapping interface. + * + * @see TypeSafeKey + * @see com.jnape.palatable.lambda.adt.hlist.HList + */ +public class HMap implements Iterable> { + + private static final HMap EMPTY = new HMap(emptyMap()); + + private final Map table; + + HMap(Map table) { + this.table = table; + } + + @SuppressWarnings("unchecked") + public Optional get(TypeSafeKey key) { + return Optional.ofNullable((T) table.get(key)); + } + + public V demand(TypeSafeKey key) { + return get(key).orElseThrow(() -> new NoSuchElementException("Demanded value for key " + key + ", but couldn't find one.")); + } + + public HMap put(TypeSafeKey key, V value) { + return alter(t -> t.put(key, value)); + } + + public HMap putAll(HMap hMap) { + return alter(t -> t.putAll(hMap.table)); + } + + public boolean containsKey(TypeSafeKey key) { + return table.containsKey(key); + } + + public Iterable keys() { + return map(Tuple2::_1, this); + } + + public Iterable values() { + return map(Tuple2::_2, this); + } + + @Override + public Iterator> iterator() { + return map(Tuple2::fromEntry, table.entrySet()).iterator(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof HMap) { + HMap that = (HMap) other; + return Objects.equals(this.table, that.table); + } + return false; + } + + @Override + public int hashCode() { + return 31 * Objects.hashCode(table); + } + + @Override + public String toString() { + return "HMap{" + + "table=" + table + + '}'; + } + + private HMap alter(Consumer> alterFn) { + HashMap copy = new HashMap<>(table); + alterFn.accept(copy); + return new HMap(copy); + } + + public static HMap emptyHMap() { + return EMPTY; + } + + public static HMap singletonHMap(TypeSafeKey key, V value) { + return new HMap(singletonMap(key, value)); + } + + public static HMap hMap(TypeSafeKey key1, V1 value1, + TypeSafeKey key2, V2 value2) { + return singletonHMap(key1, value1).put(key2, value2); + } + + public static HMap hMap(TypeSafeKey key1, V1 value1, + TypeSafeKey key2, V2 value2, + TypeSafeKey key3, V3 value3) { + return hMap(key1, value1, key2, value2).put(key3, value3); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java new file mode 100644 index 000000000..f4d27566b --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java @@ -0,0 +1,20 @@ +package com.jnape.palatable.lambda.adt.hmap; + +/** + * An interface representing a parametrized key for use in HMaps. The parameter specifies the type of the value stored + * at this binding inside the HMap. + *

+ * This is intentionally an interface so user-defined implementations are possible; however, it's important to note that + * all hopes of type-safety hinge on equality being implemented such that no two TypeSafeKeys with differing parameters + * may be considered equal. Reference equality is used here as the default, as that is sufficient. + * + * @param The type of the value that this key maps to inside an HMap + */ +@SuppressWarnings("unused") +public interface TypeSafeKey { + + static TypeSafeKey typeSafeKey() { + return new TypeSafeKey() { + }; + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java deleted file mode 100644 index e60638a5f..000000000 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.jnape.palatable.lambda.adt.tuples; - -import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; - -import java.util.Objects; - -import static java.lang.String.format; - -/** - * The binary tuple product type. Tuple2 - * - * @param <_1> The first slot element type - * @param <_2> The second slot element type - * @see Tuple3 - */ -public class Tuple2<_1, _2> implements Functor<_2>, Bifunctor<_1, _2> { - - public final _1 _1; - public final _2 _2; - - Tuple2(_1 _1, _2 _2) { - this._1 = _1; - this._2 = _2; - } - - @Override - public <_2A> Tuple2<_1, _2A> fmap(MonadicFunction fn) { - return biMapR(fn); - } - - @Override - public <_1A, _2A> Tuple2<_1A, _2A> biMap(MonadicFunction f1, - MonadicFunction f2) { - return tuple(f1.apply(_1), f2.apply(_2)); - } - - @Override - @SuppressWarnings("unchecked") - public <_1A> Tuple2<_1A, _2> biMapL(MonadicFunction fn) { - return (Tuple2<_1A, _2>) Bifunctor.super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - public <_2A> Tuple2<_1, _2A> biMapR(MonadicFunction fn) { - return (Tuple2<_1, _2A>) Bifunctor.super.biMapR(fn); - } - - @Override - public String toString() { - return format("(%s, %s)", _1, _2); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Tuple2 tuple2 = (Tuple2) o; - return Objects.equals(_1, tuple2._1) && - Objects.equals(_2, tuple2._2); - } - - @Override - public int hashCode() { - return Objects.hash(_1, _2); - } - - public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { - return new Tuple2<>(_1, _2); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java deleted file mode 100644 index a8b947414..000000000 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jnape.palatable.lambda.adt.tuples; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import java.util.Objects; - -import static java.lang.String.format; - -/** - * The ternary tuple product type. This class extends {@link Tuple2} and as such embodies the same - * functorial properties. - * - * @param <_1> The first slot element type - * @param <_2> The second slot element type - * @param <_3> The third slot element type - */ -public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { - - public final _3 _3; - - Tuple3(_1 _1, _2 _2, _3 _3) { - super(_1, _2); - this._3 = _3; - } - - @Override - @SuppressWarnings("unchecked") - public <_2A> Tuple3<_1, _2A, _3> fmap(MonadicFunction fn) { - return (Tuple3<_1, _2A, _3>) super.fmap(fn); - } - - @Override - @SuppressWarnings("unchecked") - public <_1A, _2A> Tuple3<_1A, _2A, _3> biMap(MonadicFunction f1, - MonadicFunction f2) { - return (Tuple3<_1A, _2A, _3>) super.biMap(f1, f2); - } - - @Override - @SuppressWarnings("unchecked") - public <_1A> Tuple3<_1A, _2, _3> biMapL(MonadicFunction fn) { - return (Tuple3<_1A, _2, _3>) super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - public <_2A> Tuple3<_1, _2A, _3> biMapR(MonadicFunction fn) { - return (Tuple3<_1, _2A, _3>) super.biMapR(fn); - } - - @Override - public boolean equals(Object other) { - if (other instanceof Tuple3) { - Tuple3 that = (Tuple3) other; - return super.equals(other) - && Objects.equals(this._3, that._3); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), _3); - } - - @Override - public String toString() { - return format("(%s, %s, %s)", _1, _2, _3); - } - - public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { - return new Tuple3<>(_1, _2, _3); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java index 3aed1de61..b599cf797 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functor.Profunctor; /** @@ -45,6 +45,6 @@ default DyadicFunction flip() { } default MonadicFunction, C> uncurry() { - return (ab) -> apply(ab._1, ab._2); + return (ab) -> apply(ab._1(), ab._2()); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java index db3cb325d..72423cead 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; /** * A function taking three arguments. Auto-curried @@ -34,6 +34,6 @@ default TriadicFunction flip() { @Override default DyadicFunction, C, D> uncurry() { - return (ab, c) -> apply(ab._1, ab._2, c); + return (ab, c) -> apply(ab._1(), ab._2(), c); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index 159e1f8b6..29d2846a6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.CombinatorialIterator; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index 22e2a169a..3e4330f5f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -6,7 +6,7 @@ import java.util.Optional; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; /** * Lazily generate an infinite Iterable from the successive applications of the function first to the diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java index 6a512a16e..21a015ee8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java @@ -1,17 +1,17 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; /** * Creates a Tuple2 from two values. * * @param The type of the first value; also the first slot type of returned Tuple2 * @param The type of the second value; also the second slot type of returned Tuple2 - * @see Tuple2 + * @see com.jnape.palatable.lambda.adt.hlist.Tuple2 */ public final class Tupler2 implements DyadicFunction> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java index 3a87da9ea..0a1e311a8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.UnfoldingIterator; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java index a82c89d2f..e985fd7f9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; diff --git a/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java b/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java index 3ba5547da..903aa463f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java +++ b/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java @@ -13,7 +13,7 @@ * @param The type of the second parameter * @see Functor * @see Profunctor - * @see com.jnape.palatable.lambda.adt.tuples.Tuple2 + * @see com.jnape.palatable.lambda.adt.hlist.Tuple2 */ @FunctionalInterface public interface Bifunctor { @@ -26,6 +26,6 @@ default Bifunctor biMapR(MonadicFunction fn) { return biMap(id(), fn); } - Bifunctor biMap(MonadicFunction f1, - MonadicFunction f2); + Bifunctor biMap(MonadicFunction lFn, + MonadicFunction rFn); } diff --git a/src/main/java/com/jnape/palatable/lambda/functor/Functor.java b/src/main/java/com/jnape/palatable/lambda/functor/Functor.java index 1a411061e..3f7388096 100644 --- a/src/main/java/com/jnape/palatable/lambda/functor/Functor.java +++ b/src/main/java/com/jnape/palatable/lambda/functor/Functor.java @@ -13,7 +13,7 @@ * @see Bifunctor * @see Profunctor * @see MonadicFunction - * @see com.jnape.palatable.lambda.adt.tuples.Tuple2 + * @see com.jnape.palatable.lambda.adt.hlist.Tuple2 * @see com.jnape.palatable.lambda.adt.Either */ @FunctionalInterface diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java index 526cbd8c9..5e4a4dcd4 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; public class CombinatorialIterator extends ImmutableIterator> { private final Iterator asIterator; diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java index 3b34b9ed7..7a5ac3585 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; import java.util.NoSuchElementException; @@ -26,8 +26,8 @@ public A next() { throw new NoSuchElementException(); Tuple2 acc = optionalAcc.get(); - A next = acc._1; - optionalAcc = function.apply(acc._2); + A next = acc._1(); + optionalAcc = function.apply(acc._2()); return next; } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java index 2950ed5b0..964e5ddfa 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -6,6 +6,8 @@ import org.junit.rules.ExpectedException; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static com.jnape.palatable.lambda.adt.Either.fromOptional; import static com.jnape.palatable.lambda.adt.Either.left; @@ -158,4 +160,35 @@ public void dyadicTryingLiftsCheckedSupplierMappingAnyThrownExceptions() { throw new Exception("expected"); }, Throwable::getMessage)); } + + @Test + public void monadicPeekLiftsIOToTheRight() { + Either left = left("foo"); + Either right = right(1); + + AtomicInteger intRef = new AtomicInteger(); + + left.peek(intRef::set); + assertEquals(0, intRef.get()); + + right.peek(intRef::set); + assertEquals(1, intRef.get()); + } + + @Test + public void dyadicPeekDuallyLiftsIO() { + Either left = left("foo"); + Either right = right(1); + + AtomicReference stringRef = new AtomicReference<>(); + AtomicInteger intRef = new AtomicInteger(); + + left.peek(stringRef::set, intRef::set); + assertEquals("foo", stringRef.get()); + assertEquals(0, intRef.get()); + + right.peek(stringRef::set, intRef::set); + assertEquals("foo", stringRef.get()); + assertEquals(1, intRef.get()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java deleted file mode 100644 index bdac78d81..000000000 --- a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.jnape.palatable.lambda.adt; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.adt.HList.cons; -import static com.jnape.palatable.lambda.adt.HList.list; -import static com.jnape.palatable.lambda.adt.HList.nil; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -public class HListTest { - - @Test - public void head() { - assertEquals("head", nil().cons("head").head()); - assertEquals("new head", nil().cons("old head").cons("new head").head()); - } - - @Test - public void tail() { - assertEquals(nil(), nil().cons("head").tail()); - assertEquals(nil().cons("old head"), nil().cons("old head").cons("new head").tail()); - } - - @Test - public void convenienceStaticFactoryMethods() { - assertEquals(nil().cons(1), cons(1, nil())); - assertEquals(nil().cons(1), list(1)); - assertEquals(nil().cons('2').cons(1), list(1, '2')); - assertEquals(nil().cons("3").cons('2').cons(1), list(1, '2', "3")); - assertEquals(nil().cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0)); - assertEquals(nil().cons(false).cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0, false)); - } - - @Test - public void functorialProperties() { - assertEquals(list("1"), list(1).fmap(Object::toString)); - } - - @Test - public void nilReusesInstance() { - assertSame(nil(), nil()); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java new file mode 100644 index 000000000..425fb9a02 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java @@ -0,0 +1,65 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.cons; +import static com.jnape.palatable.lambda.adt.hlist.HList.nil; +import static com.jnape.palatable.lambda.adt.hlist.HList.singleton; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class HListTest { + + @Test + public void head() { + assertEquals("head", nil().cons("head").head()); + assertEquals("new head", nil().cons("old head").cons("new head").head()); + } + + @Test + public void tail() { + assertEquals(nil(), nil().cons("head").tail()); + assertEquals(nil().cons("old head"), nil().cons("old head").cons("new head").tail()); + } + + @Test + public void convenienceStaticFactoryMethods() { + assertEquals(nil().cons(1), cons(1, nil())); + assertEquals(nil().cons(1), singleton(1)); + assertEquals(nil().cons('2').cons(1), tuple(1, '2')); + assertEquals(nil().cons("3").cons('2').cons(1), tuple(1, '2', "3")); + assertEquals(nil().cons(4.0).cons("3").cons('2').cons(1), tuple(1, '2', "3", 4.0)); + assertEquals(nil().cons(false).cons(4.0).cons("3").cons('2').cons(1), tuple(1, '2', "3", 4.0, false)); + } + + @Test + public void nilReusesInstance() { + assertSame(nil(), nil()); + } + + @Test + @SuppressWarnings({"EqualsWithItself", "EqualsBetweenInconvertibleTypes"}) + public void equality() { + assertTrue(nil().equals(nil())); + assertTrue(cons(1, nil()).equals(cons(1, nil()))); + + assertFalse(cons(1, nil()).equals(nil())); + assertFalse(nil().equals(cons(1, nil()))); + + assertFalse(cons(1, cons(2, nil())).equals(cons(1, nil()))); + assertFalse(cons(1, nil()).equals(cons(1, cons(2, nil())))); + } + + @Test + public void hashCodeUsesDecentDistribution() { + assertEquals(nil().hashCode(), nil().hashCode()); + assertEquals(nil().cons(1).hashCode(), nil().cons(1).hashCode()); + + assertNotEquals(nil().cons(1).hashCode(), nil().cons(2).hashCode()); + assertNotEquals(nil().cons(1).cons(2).hashCode(), nil().cons(1).cons(3).hashCode()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonTest.java new file mode 100644 index 000000000..3b062d372 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonTest.java @@ -0,0 +1,37 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import org.junit.Before; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.nil; +import static org.junit.Assert.assertEquals; + +public class SingletonTest { + + private Singleton singleton; + + @Before + public void setUp() { + singleton = new Singleton<>(1); + } + + @Test + public void head() { + assertEquals((Integer) 1, singleton.head()); + } + + @Test + public void tail() { + assertEquals(nil(), singleton.tail()); + } + + @Test + public void cons() { + assertEquals(new Tuple2<>("0", singleton), singleton.cons("0")); + } + + @Test + public void functorProperties() { + assertEquals(new Singleton<>("1"), singleton.fmap(Object::toString)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java new file mode 100644 index 000000000..d74518bba --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java @@ -0,0 +1,71 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.junit.Assert.assertEquals; + +public class Tuple2Test { + + private Tuple2 tuple2; + + @Before + public void setUp() throws Exception { + tuple2 = new Tuple2<>(1, new Singleton<>(2)); + } + + @Test + public void head() { + assertEquals((Integer) 1, tuple2.head()); + } + + @Test + public void tail() { + assertEquals(new Singleton<>(2), tuple2.tail()); + } + + @Test + public void cons() { + assertEquals(new Tuple3<>(0, tuple2), tuple2.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Integer) 1, tuple2._1()); + assertEquals((Integer) 2, tuple2._2()); + } + + @Test + public void functorProperties() { + assertEquals(new Tuple2<>(1, new Singleton<>("2")), tuple2.fmap(Object::toString)); + } + + @Test + public void bifunctorProperties() { + assertEquals(new Tuple2<>("1", new Singleton<>("2")), tuple2.biMap(Object::toString, Object::toString)); + } + + @Test + public void mapEntryProperties() { + assertEquals((Integer) 1, tuple2.getKey()); + assertEquals((Integer) 2, tuple2.getValue()); + } + + @Test(expected = UnsupportedOperationException.class) + public void setValueIsNotSupported() { + tuple2.setValue(3); + } + + @Test + public void staticFactoryMethodFromMapEntry() { + Map.Entry stringIntEntry = new HashMap() {{ + put("string", 1); + }}.entrySet().iterator().next(); + + assertEquals(tuple("string", 1), Tuple2.fromEntry(stringIntEntry)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java new file mode 100644 index 000000000..9aa335811 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java @@ -0,0 +1,48 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Tuple3Test { + + private Tuple3 tuple3; + + @Before + public void setUp() { + tuple3 = new Tuple3<>(1, new Tuple2<>("2", new Singleton<>('3'))); + } + + @Test + public void head() { + assertEquals((Integer) 1, tuple3.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple2<>("2", new Singleton<>('3')), tuple3.tail()); + } + + @Test + public void cons() { + assertEquals(new Tuple4<>(0, tuple3), tuple3.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Integer) 1, tuple3._1()); + assertEquals("2", tuple3._2()); + assertEquals((Character) '3', tuple3._3()); + } + + @Test + public void functorProperties() { + assertEquals(new Tuple3<>(1, new Tuple2<>("2", new Singleton<>("3"))), tuple3.fmap(Object::toString)); + } + + @Test + public void bifunctorProperties() { + assertEquals(new Tuple3<>(1, new Tuple2<>(2, new Singleton<>("3"))), tuple3.biMap(Integer::parseInt, Object::toString)); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java new file mode 100644 index 000000000..1213ec5a5 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java @@ -0,0 +1,50 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Tuple4Test { + + private Tuple4 tuple4; + + @Before + public void setUp() { + tuple4 = new Tuple4<>(1, new Tuple3<>("2", new Tuple2<>('3', new Singleton<>(false)))); + } + + @Test + public void head() { + assertEquals((Integer) 1, tuple4.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple3<>("2", new Tuple2<>('3', new Singleton<>(false))), tuple4.tail()); + } + + @Test + public void cons() { + assertEquals(new Tuple5<>(0, tuple4), tuple4.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Integer) 1, tuple4._1()); + assertEquals("2", tuple4._2()); + assertEquals((Character) '3', tuple4._3()); + assertEquals(false, tuple4._4()); + } + + @Test + public void functorProperties() { + assertEquals(new Tuple4<>(1, new Tuple3<>("2", new Tuple2<>('3', new Singleton<>(true)))), tuple4.fmap(x -> !x)); + } + + @Test + public void bifunctorProperties() { + assertEquals(new Tuple4<>(1, new Tuple3<>("2", new Tuple2<>("3", new Singleton<>(true)))), + tuple4.biMap(Object::toString, x -> !x)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java new file mode 100644 index 000000000..2745f3c47 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java @@ -0,0 +1,53 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Tuple5Test { + + private Tuple5 tuple5; + + @Before + public void setUp() { + tuple5 = new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new Singleton<>(5L))))); + } + + @Test + public void head() { + assertEquals((Integer) 1, tuple5.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new Singleton<>(5L)))), tuple5.tail()); + } + + @Test + public void cons() { + assertEquals(new HCons<>(0, tuple5), tuple5.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Integer) 1, tuple5._1()); + assertEquals("2", tuple5._2()); + assertEquals((Character) '3', tuple5._3()); + assertEquals(false, tuple5._4()); + assertEquals((Long) 5L, tuple5._5()); + } + + @Test + public void functorProperties() { + assertEquals(new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new Singleton<>("5"))))), + tuple5.fmap(Object::toString)); + } + + @Test + public void bifunctorProperties() { + assertEquals(new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(true, new Singleton<>("5"))))), + tuple5.biMap(x -> !x, Object::toString)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java new file mode 100644 index 000000000..c89861eea --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hmap/HMapTest.java @@ -0,0 +1,173 @@ +package com.jnape.palatable.lambda.adt.hmap; + +import org.junit.Test; + +import java.util.NoSuchElementException; +import java.util.Optional; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.adt.hmap.HMap.emptyHMap; +import static com.jnape.palatable.lambda.adt.hmap.HMap.hMap; +import static com.jnape.palatable.lambda.adt.hmap.HMap.singletonHMap; +import static com.jnape.palatable.lambda.adt.hmap.TypeSafeKey.typeSafeKey; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static testsupport.matchers.IterableMatcher.iterates; + +public class HMapTest { + + @Test + public void getForPresentKey() { + TypeSafeKey stringKey = typeSafeKey(); + assertEquals(Optional.of("string value"), + new HMap(singletonMap(stringKey, "string value")).get(stringKey)); + } + + @Test + public void getForAbsentKey() { + assertEquals(Optional.empty(), + new HMap(singletonMap(typeSafeKey(), "string value")).get(typeSafeKey())); + } + + @Test + public void getForPresentKeyWithNullValue() { + TypeSafeKey stringKey = typeSafeKey(); + assertEquals(Optional.empty(), + new HMap(singletonMap(stringKey, null)).get(stringKey)); + } + + @Test + public void put() { + TypeSafeKey stringKey = typeSafeKey(); + assertEquals(new HMap(singletonMap(stringKey, "string value")), + emptyHMap().put(stringKey, "string value")); + + assertEquals(new HMap(singletonMap(stringKey, "new value")), + emptyHMap() + .put(stringKey, "string value") + .put(stringKey, "new value")); + } + + @Test + public void putAll() { + TypeSafeKey stringKey1 = typeSafeKey(); + TypeSafeKey stringKey2 = typeSafeKey(); + TypeSafeKey intKey = typeSafeKey(); + + HMap left = hMap(stringKey1, "string value", + intKey, 1); + HMap right = hMap(stringKey2, "another string value", + intKey, 2); + + assertEquals(hMap(stringKey1, "string value", + stringKey2, "another string value", + intKey, 2), + left.putAll(right)); + assertEquals(hMap(stringKey1, "string value", + stringKey2, "another string value", + intKey, 1), + right.putAll(left)); + } + + @Test + public void containsKey() { + TypeSafeKey stringKey1 = typeSafeKey(); + TypeSafeKey stringKey2 = typeSafeKey(); + TypeSafeKey intKey = typeSafeKey(); + + HMap hMap = singletonHMap(stringKey1, "string"); + + assertTrue(hMap.containsKey(stringKey1)); + assertFalse(hMap.containsKey(stringKey2)); + assertFalse(hMap.containsKey(intKey)); + } + + @Test + public void demandForPresentKey() { + TypeSafeKey stringKey = typeSafeKey(); + assertEquals("string value", + singletonHMap(stringKey, "string value").demand(stringKey)); + } + + @Test(expected = NoSuchElementException.class) + public void demandForAbsentKey() { + emptyHMap().demand(typeSafeKey()); + } + + @Test + public void iteratesKVPairsAsTuples() { + TypeSafeKey stringKey = typeSafeKey(); + + assertThat(singletonHMap(stringKey, "string value"), + iterates(tuple(stringKey, "string value"))); + } + + @Test + public void keys() { + TypeSafeKey stringKey = typeSafeKey(); + + assertThat(singletonHMap(stringKey, "string value").keys(), + iterates(stringKey)); + } + + @Test + public void values() { + assertThat(singletonHMap(typeSafeKey(), "string value").values(), + iterates("string value")); + } + + @Test + public void convenienceStaticFactoryMethods() { + TypeSafeKey stringKey = typeSafeKey(); + TypeSafeKey intKey = typeSafeKey(); + TypeSafeKey floatKey = typeSafeKey(); + assertEquals(new HMap(emptyMap()), HMap.emptyHMap()); + assertEquals(new HMap(singletonMap(stringKey, "string value")), HMap.singletonHMap(stringKey, "string value")); + assertEquals(emptyHMap().put(stringKey, "string value").put(intKey, 1), + hMap(stringKey, "string value", + intKey, 1)); + assertEquals(emptyHMap().put(stringKey, "string value").put(intKey, 1).put(floatKey, 1f), + hMap(stringKey, "string value", + intKey, 1, + floatKey, 1f)); + } + + @Test + @SuppressWarnings("EqualsWithItself") + public void equality() { + assertTrue(emptyHMap().equals(emptyHMap())); + + TypeSafeKey stringKey = typeSafeKey(); + assertTrue(emptyHMap().put(stringKey, "one").equals(emptyHMap().put(stringKey, "one"))); + + assertFalse(emptyHMap().equals(emptyHMap().put(stringKey, "string key"))); + assertFalse(emptyHMap().put(stringKey, "string key").equals(emptyHMap())); + assertFalse(emptyHMap().put(typeSafeKey(), "one").equals(emptyHMap().put(typeSafeKey(), "one"))); + assertFalse(emptyHMap().put(typeSafeKey(), "one").equals(emptyHMap().put(typeSafeKey(), 1))); + assertFalse(emptyHMap().put(typeSafeKey(), 1).equals(emptyHMap().put(typeSafeKey(), "one"))); + } + + @Test + public void hashCodeUsesDecentDistribution() { + assertEquals(emptyHMap().hashCode(), emptyHMap().hashCode()); + TypeSafeKey stringKey = typeSafeKey(); + assertEquals(new HMap(singletonMap(stringKey, "string value")).hashCode(), + new HMap(singletonMap(stringKey, "string value")).hashCode()); + + assertNotEquals(emptyHMap(), new HMap(singletonMap(stringKey, "string value"))); + assertNotEquals(new HMap(singletonMap(stringKey, "string value")), + new HMap(singletonMap(stringKey, "another string value"))); + + } + + @Test + public void emptyHMapReusesInstance() { + assertSame(emptyHMap(), emptyHMap()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java deleted file mode 100644 index 4a183cd5e..000000000 --- a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.jnape.palatable.lambda.adt.tuples; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; - -public class Tuple2Test { - - @Test - public void hasTwoSlots() { - Tuple2 stringIntTuple = tuple("foo", 1); - - assertThat(stringIntTuple._1, is("foo")); - assertThat(stringIntTuple._2, is(1)); - } - - @Test - public void toStringIsReasonable() { - assertThat(tuple("1", "2").toString(), is("(1, 2)")); - } - - @Test - public void usesValueBasedEquality() { - Tuple2 same = tuple("a", 1); - Tuple2 alsoSame = tuple("a", 1); - Tuple2 different = tuple("b", 2); - - assertEquals(same, alsoSame); - assertEquals(alsoSame, same); - - assertNotEquals(same, different); - assertNotEquals(alsoSame, different); - assertNotEquals(different, same); - assertNotEquals(different, alsoSame); - - assertNotEquals(same, new Object()); - } - - @Test - public void hashesCorrectlyForEqualTuples() { - assertEquals(tuple("foo", 1).hashCode(), tuple("foo", 1).hashCode()); - } - - @Test - public void hashesUsingReasonableDistribution() { - assertNotEquals(tuple("foo", 1).hashCode(), tuple("bar", 2).hashCode()); - } - - @Test - public void functorialProperties() { - assertThat(tuple("foo", 1).fmap(String::valueOf), is(tuple("foo", "1"))); - } - - @Test - public void biFunctorialProperties() { - assertThat(tuple("foo", 1).biMapL(String::toUpperCase), is(tuple("FOO", 1))); - assertThat(tuple("foo", 1).biMapR(String::valueOf), is(tuple("foo", "1"))); - assertThat(tuple("foo", 1).biMap(String::toUpperCase, i -> i + 1), is(tuple("FOO", 2))); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java deleted file mode 100644 index e1cfc07bf..000000000 --- a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.jnape.palatable.lambda.adt.tuples; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.adt.tuples.Tuple3.tuple; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; - -public class Tuple3Test { - - @Test - public void hasThreeSlots() { - Tuple3 integerStringCharacterTuple = tuple(1, "two", '3'); - - assertThat(integerStringCharacterTuple._1, is(1)); - assertThat(integerStringCharacterTuple._2, is("two")); - assertThat(integerStringCharacterTuple._3, is('3')); - } - - @Test - public void usesValueBasedEquality() { - Tuple3 same = tuple("a", "b", 1); - Tuple3 alsoSame = tuple("a", "b", 1); - Tuple3 different = tuple("b", "b", 2); - - assertEquals(same, alsoSame); - assertEquals(alsoSame, same); - - assertNotEquals(same, different); - assertNotEquals(alsoSame, different); - assertNotEquals(different, same); - assertNotEquals(different, alsoSame); - - assertNotEquals(same, new Object()); - } - - @Test - public void hashesCorrectlyForEqualTuples() { - assertEquals(tuple("foo", "bar", 1).hashCode(), tuple("foo", "bar", 1).hashCode()); - } - - @Test - public void hashesUsingReasonableDistribution() { - assertNotEquals(tuple("foo", "bar", 1).hashCode(), tuple("bar", "baz", 2).hashCode()); - } - - @Test - public void toStringIsReasonable() { - assertThat(tuple("foo", "bar", "baz").toString(), is("(foo, bar, baz)")); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java index dac5f6999..8ec963682 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java index 115e61d0e..fd0d2478c 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java index af678b616..d2813740e 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; @@ -11,7 +11,7 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.CartesianProduct.cartesianProduct; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java index be0eb7a28..b909c6feb 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Tupler2.tupler; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java index 7a9722d73..9d53ee2f9 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java @@ -11,7 +11,7 @@ import java.util.Optional; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Take.take; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Unfoldr.unfoldr; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java index 5955a417f..14efd3d28 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; @@ -12,7 +12,7 @@ import java.util.Arrays; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java index d9b27f864..604a0ff15 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions.builtin.triadic; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.traitor.annotations.TestTraits; @@ -11,7 +11,7 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; import static java.util.Arrays.asList; diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java index b7449b298..ecfa8faab 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java @@ -1,6 +1,5 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -9,6 +8,7 @@ import java.util.Iterator; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.mockito.Mockito.when; @@ -56,9 +56,9 @@ public void computesCombinationsInOrder() { mockIteratorToHaveValues(as, "a1", "a2"); mockIteratorToHaveValues(bs, "b1", "b2"); - assertThat(combinatorialIterator.next(), is(Tuple2.tuple("a1", "b1"))); - assertThat(combinatorialIterator.next(), is(Tuple2.tuple("a1", "b2"))); - assertThat(combinatorialIterator.next(), is(Tuple2.tuple("a2", "b1"))); - assertThat(combinatorialIterator.next(), is(Tuple2.tuple("a2", "b2"))); + assertThat(combinatorialIterator.next(), is(tuple("a1", "b1"))); + assertThat(combinatorialIterator.next(), is(tuple("a1", "b2"))); + assertThat(combinatorialIterator.next(), is(tuple("a2", "b1"))); + assertThat(combinatorialIterator.next(), is(tuple("a2", "b2"))); } } diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java index 0029e5e69..85574e661 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; import org.junit.Test; import java.util.Optional; -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java index 86fa80278..191df47c6 100644 --- a/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java +++ b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java @@ -17,10 +17,10 @@ public InvocationRecordingBifunctor(AtomicReference leftFn, @Override @SuppressWarnings("unchecked") - public Bifunctor biMap(MonadicFunction f1, - MonadicFunction f2) { - leftFn.set(f1); - rightFn.set(f2); + public Bifunctor biMap(MonadicFunction lFn, + MonadicFunction rFn) { + leftFn.set(lFn); + rightFn.set(rFn); return (Bifunctor) this; } }