Either<L, R>s into a single
+ * Given two binary operators over L and R, merge multiple Either<L, R>s into a single
* Either<L, R>. Note that merge biases towards left values; that is, if any left
* value exists, the result will be a left value, such that only unanimous right values result in an ultimate right
* value.
*
* @param leftFn the binary operator for L
* @param rightFn the binary operator for R
- * @param other the other Either
+ * @param others the other Eithers to merge into this one
* @return the merged Either
*/
+ @SafeVarargs
public final EitherHList.
+ *
+ * @param the head element type
+ * @param HList; returns an HNil if this is the last element.
+ *
+ * @return the tail
+ */
public Tail tail() {
return tail;
}
@@ -180,6 +196,9 @@ public final String toString() {
}
}
+ /**
+ * The empty HList.
+ */
public static final class HNil extends HListhMap in this HMap.
*
- * @param hMap the other hMap
+ * @param hMap the other HMap
* @return the updated HMap
*/
public HMap putAll(HMap hMap) {
@@ -87,6 +87,26 @@ public boolean containsKey(TypeSafeKey key) {
return table.containsKey(key);
}
+ /**
+ * Remove a mapping from this HMap.
+ *
+ * @param key the key
+ * @return the updated HMap
+ */
+ public HMap remove(TypeSafeKey key) {
+ return alter(t -> t.remove(key));
+ }
+
+ /**
+ * Remove all the key/value mappings in hMap from this HMap.
+ *
+ * @param hMap the other HMap
+ * @return the updated HMap
+ */
+ public HMap removeAll(HMap hMap) {
+ return alter(t -> t.keySet().removeAll(hMap.table.keySet()));
+ }
+
/**
* Retrieve all the mapped keys.
*
diff --git a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Const.java b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Const.java
new file mode 100644
index 000000000..e19261415
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Const.java
@@ -0,0 +1,88 @@
+package com.jnape.palatable.lambda.functor.builtin;
+
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.function.Function;
+
+/**
+ * A (surprisingly useful) functor over some phantom type B, retaining a value of type A that
+ * can be retrieved later. This is useful in situations where it is desirable to retain constant information throughout
+ * arbitrary functor transformations, such that at the end of the chain, regardless of how B has been
+ * altered, A is still pristine and retrievable.
+ *
+ * @param the left parameter type, and the type of the stored value
+ * @param the right (phantom) parameter type
+ */
+public final class Const implements Functor, Bifunctor {
+
+ private final A a;
+
+ public Const(A a) {
+ this.a = a;
+ }
+
+ /**
+ * Retrieve the stored value.
+ *
+ * @return the value
+ */
+ public A runConst() {
+ return a;
+ }
+
+ /**
+ * Map over the right parameter. Note that because B is never actually known quantity outside of a type
+ * signature, this is effectively a no-op that serves only to alter Const's type signature.
+ *
+ * @param fn the mapping function
+ * @param Const.
+ *
+ * @param lFn the left parameter mapping function
+ * @param rFn the right parameter mapping function
+ * @param A that can be mapped over and retrieved later.
+ *
+ * @param the value type
+ */
+public final class Identity implements Functor {
+
+ private final A a;
+
+ public Identity(A a) {
+ this.a = a;
+ }
+
+ /**
+ * Retrieve the value.
+ *
+ * @return the value
+ */
+ public A runIdentity() {
+ return a;
+ }
+
+ /**
+ * Covariantly map over the value.
+ *
+ * @param fn the mapping function
+ * @param the new value type
+ * @return an Identity over B (the new value)
+ */
+ @Override
+ public Identity fmap(Function super A, ? extends B> fn) {
+ return new Identity<>(fn.apply(a));
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/lens/Lens.java b/src/main/java/com/jnape/palatable/lambda/lens/Lens.java
new file mode 100644
index 000000000..6717baefc
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/lens/Lens.java
@@ -0,0 +1,273 @@
+package com.jnape.palatable.lambda.lens;
+
+import com.jnape.palatable.lambda.functions.Fn2;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
+import static com.jnape.palatable.lambda.lens.functions.Over.over;
+import static com.jnape.palatable.lambda.lens.functions.Set.set;
+import static com.jnape.palatable.lambda.lens.functions.View.view;
+
+/**
+ * An approximation of van Laarhoven lenses.
+ *
+ * A "lens" can be considered in its simplest form as the conjugation of a "getter" and a "setter"; that is, a
+ * unification type representing the way to retrieve a "smaller" value A from a "larger" value
+ * S, as well as a way to update a "smaller" value B of a "larger" value S,
+ * producing another "larger" value T.
+ *
+ * Consider the following example: + *
+ * {@code
+ * public final class Person {
+ * private final int age;
+ *
+ * public Person(int age) {
+ * this.age = age;
+ * }
+ *
+ * public int getAge() {
+ * return age;
+ * }
+ *
+ * public Person setAge(int age) {
+ * return new Person(age);
+ * }
+ * }
+ * }
+ *
+ * A lens that focused on the age field of an instance of Person might look like this:
+ *
+ * {@code
+ * Lens ageLens = Lens.lens(Person::getAge, Person::setAge);
+ *
+ * Person adult = new Person(18);
+ * Integer age = view(ageLens, adult); // 18
+ *
+ * Person olderAdult = set(ageLens, 19, adult);
+ * Integer olderAge = view(ageLens, olderAdult); // 19
+ * }
+ *
+ * The pattern of a getter and setter that mutually agree on both A and B as well as on both
+ * S and T is so common that this can be given a simplified type signature:
+ *
+ * {@code
+ * Lens.Simple ageLens = Lens.simpleLens(Person::getAge, Person::setAge);
+ *
+ * Person adult = new Person(18);
+ * Integer age = view(ageLens, adult); // 18
+ *
+ * Person olderAdult = set(ageLens, 19, adult);
+ * Integer olderAge = view(ageLens, olderAdult); // 19
+ * }
+ *
+ * However, consider if age could be updated on a Person by being provided a date of birth, in
+ * the form of a LocalDate:
+ *
+ * {@code
+ * public final class Person {
+ * private final int age;
+ *
+ * public Person(int age) {
+ * this.age = age;
+ * }
+ *
+ * public int getAge() {
+ * return age;
+ * }
+ *
+ * public Person setAge(int age) {
+ * return new Person(age);
+ * }
+ *
+ * public Person setAge(LocalDate dob) {
+ * return setAge((int) YEARS.between(dob, LocalDate.now()));
+ * }
+ * }
+ * }
+ *
+ * This is why Lens has both an A and a B: A is the value for "getting", and
+ * B is the potentially different value for "setting". This distinction makes lenses powerful enough to
+ * express the more complicated setAge case naturally:
+ *
+ * {@code
+ * Lens ageDobLens = Lens.lens(Person::getAge, Person::setAge);
+ *
+ * Person adult = new Person(18);
+ * Integer age = view(ageDobLens, adult); // 18
+ *
+ * Person olderAdult = set(ageDobLens, LocalDate.of(1997, 1, 1), adult);
+ * Integer olderAge = view(ageDobLens, olderAdult); // 19 at the time of this writing...anyone else feel old?
+ * }
+ *
+ * Additionally, we might imagine a lens that produces a different "larger" value on updating than what was given.
+ * Consider a lens that reads the first string from a list, but produces a Set of strings on update:
+ *
+ * {@code
+ * Lens, Set, String, String> lens = Lens.lens(
+ * l -> l.get(0),
+ * (l, s) -> {
+ * List copy = new ArrayList<>(l);
+ * copy.set(0, s);
+ * return new HashSet<>(copy);
+ * });
+ *
+ * String firstElement = view(lens, asList("foo", "bar")); // "foo
+ * System.out.println(firstElement);
+ *
+ * set(lens, "oof", asList("foo", "bar")); // ["bar", "oof"]
+ * set(lens, "bar", asList("foo", "bar")); // ["bar"]
+ * }
+ *
+ * For more information, learn
+ * about
+ * lenses.
+ *
+ * @param
+ * Although the Java type system does not allow enforceability, the functor instance FT should be the same as FB,
+ * only differentiating in their parameters.
+ *
+ * @param
+ * This function is similar to {@link Set}, except that it allows the setting value
+ * More idiomatically, this function can be used to treat a lens as a "setter" of
+ * More idiomatically, this function can be used to treat a lens as a "getter" of fix() {
+ return this::apply;
+ }
+
+ @Override
+ default Lens fmap(Function super T, ? extends U> fn) {
+ return this.compose(Lens.lens(id(), (s, t) -> fn.apply(t)));
+ }
+
+ /**
+ * Left-to-right composition of lenses. Requires compatibility between S and T.
+ *
+ * @param f the other lens
+ * @param andThen(Lens f) {
+ return f.compose(this);
+ }
+
+ /**
+ * Right-to-left composition of lenses. Requires compatibility between A and B.
+ *
+ * @param g the other lens
+ * @param the new "larger" value for reading (previously S)
+ * @param
Lens
compose(Lens
g) {
+ return lens(view(g).fmap(view(this)), (q, b) -> over(g, set(this, b), q));
+ }
+
+ /**
+ * Static factory method for creating a lens from a getter function and a setter function.
+ *
+ * @param getter the getter function
+ * @param setter the setter function
+ * @param
The type of the "larger" value for reading
+ * @param Lens lens(Function super S, ? extends A> getter,
+ BiFunction super S, ? super B, ? extends T> setter) {
+ return new Lens() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public The type of both "larger" values
+ * @param The type of both "smaller" values
+ * @return the lens
+ */
+ @SuppressWarnings("unchecked")
+ static Lens.Simple simpleLens(Function super S, ? extends A> getter,
+ BiFunction super S, ? super A, ? extends S> setter) {
+ return lens(getter, setter)::apply;
+ }
+
+ /**
+ * A convenience type with a simplified type signature for common lenses with both unified "larger" values and
+ * unified "smaller" values.
+ *
+ * @param The type of both "larger" values
+ * @param The type of both "smaller" values
+ */
+ @FunctionalInterface
+ interface Simple extends Lens {
+
+ @Override
+ default fix() {
+ return this::apply;
+ }
+
+ @SuppressWarnings("unchecked")
+ default Lens.Simple
compose(Lens.Simple
g) {
+ return Lens.super.compose(g)::apply;
+ }
+
+ default Lens.Simple
andThen(Lens.Simple f) {
+ return f.compose(this);
+ }
+
+ /**
+ * A convenience type with a simplified type signature for fixed simple lenses.
+ *
+ * @param The type of both "larger" values
+ * @param The type of both "smaller" values
+ * @param , FA extends Functor>
+ extends Lens.Fixed {
+ }
+ }
+
+ /**
+ * A lens that has been fixed to a functor. Because the lens is no longer polymorphic, it can additionally be safely
+ * represented as an Fn2.
+ *
+ * @param The type of the "larger" value for reading
+ * @param , FB extends Functor>
+ extends Fn2A to B, and a "larger" value S, produce a
+ * T by retrieving the A from the S, applying the function, and updating the
+ * S with the B resulting from the function.
+ * B to be derived from
+ * S via function application, rather than provided.
+ *
+ * @param the type of the larger value
+ * @param implements Fn3 lens, Function super A, ? extends B> fn, S s) {
+ return lens. Over over() {
+ return new Over<>();
+ }
+
+ public static Fn2 lens) {
+ return Over.over().apply(lens);
+ }
+
+ public static Fn1 over(Lens lens,
+ Function super A, ? extends B> fn) {
+ return over(lens).apply(fn);
+ }
+
+ public static T over(Lens lens, Function super A, ? extends B> fn, S s) {
+ return over(lens, fn).apply(s);
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java b/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java
new file mode 100644
index 000000000..aed093e8f
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java
@@ -0,0 +1,51 @@
+package com.jnape.palatable.lambda.lens.functions;
+
+import com.jnape.palatable.lambda.functions.Fn1;
+import com.jnape.palatable.lambda.functions.Fn2;
+import com.jnape.palatable.lambda.functions.Fn3;
+import com.jnape.palatable.lambda.functor.builtin.Identity;
+import com.jnape.palatable.lambda.lens.Lens;
+
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
+import static com.jnape.palatable.lambda.lens.functions.Over.over;
+
+/**
+ * Given a lens, a "smaller" value B, and a "larger" value S, produce a T by
+ * lifting the lens into {@link Identity}.
+ * Bs on Ss,
+ * potentially producing a different "larger" value, T.
+ *
+ * @param the type of the larger value
+ * @param implements Fn3 lens, B b, S s) {
+ return over(lens, constantly(b), s);
+ }
+
+ public static Set set() {
+ return new Set<>();
+ }
+
+ public static Fn2 set(Lens lens) {
+ return Set.set().apply(lens);
+ }
+
+ public static Fn1 set(Lens lens, B b) {
+ return set(lens).apply(b);
+ }
+
+ public static T set(Lens lens, B b, S s) {
+ return set(lens, b).apply(s);
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java b/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java
new file mode 100644
index 000000000..966debeff
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java
@@ -0,0 +1,42 @@
+package com.jnape.palatable.lambda.lens.functions;
+
+import com.jnape.palatable.lambda.functions.Fn1;
+import com.jnape.palatable.lambda.functions.Fn2;
+import com.jnape.palatable.lambda.functor.builtin.Const;
+import com.jnape.palatable.lambda.lens.Lens;
+
+/**
+ * Given a lens and a "larger" value S, retrieve a "smaller" value A by lifting the lens into
+ * {@link Const}.
+ * As from Ss.
+ *
+ * @param the type of the larger value
+ * @param implements Fn2 lens, S s) {
+ return lens. View view() {
+ return new View<>();
+ }
+
+ public static Fn1 view(Lens lens) {
+ return View.view().apply(lens);
+ }
+
+ public static A view(Lens lens, S s) {
+ return view(lens).apply(s);
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java
new file mode 100644
index 000000000..5bcf16315
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java
@@ -0,0 +1,63 @@
+package com.jnape.palatable.lambda.lens.lenses;
+
+import com.jnape.palatable.lambda.lens.Lens;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static com.jnape.palatable.lambda.lens.Lens.simpleLens;
+
+/**
+ * Lenses that operate on {@link Collection}s.
+ */
+public final class CollectionLens {
+
+ private CollectionLens() {
+ }
+
+ /**
+ * Convenience static factory method for creating a lens that focuses on a copy of a Collection, given
+ * a function that creates the copy. Useful for composition to avoid mutating a Collection reference.
+ *
+ * @param copyFn the copying function
+ * @param , List
, List