+ * Comonad laws: + *
w.extend(wa -> wa.extract()).equals(w)w.extend(f).extract().equals(f.apply(w))w.extend(f).extend(g).equals(w.extend(wa -> g(wa.extend(f))))
+ * For example, think of blurring an image, where the new pixel relies on the surrounding pixels, or of producing the next step in a generic cellular automaton.
+ *
+ * @param f the function using the global state Comonad<A, W> to produce a B
+ * @param the resulting B at each point in the resulting Comonad<B, W>
+ * @param
+ * Essentially, for the case of a non-empty data structure with a cursor, this produces that data structure with the cursor at all possible locations.
+ *
+ * It may be worth finding a way to do this lazily, as a Comonad can often be thought of as representing a state space (potentially infinite), with a Monad then used to traverse a specific path through it.
+ *
+ * `extend` is left to be implemented, and `duplicate` is given a definition based off of it, in symmetry with `Monad`.
+ * However, it might be better to reverse this for `Comonad`, as there may be a more natural definition for `duplicate` in an implementation for the interface.
+ *
+ *
+ * @param w the Comonad to be duplicated
+ * @param the value type of the Comonad
+ * @param
+ * Example:
+ * the type of the storage and the cursor
+ * @param the type of the value produced
+ */
+public interface ComonadStore> extends Comonad {
+ /**
+ * Retrieve a value from a mapped cursor.
+ *
+ * @param f the relative change to make to the cursor before retrieving a value
+ * @return a retrieved value A
+ */
+ A peeks(Fn1 super S, ? extends S> f);
+
+ /**
+ * Retrieve a value from a cursor.
+ *
+ * @param s the cursor to extract the value from
+ * @return a retrieved value A
+ */
+ default A peek(S s) {
+ return peeks(constantly(s));
+ };
+
+ /**
+ * Retrieve a value from the current cursor.
+ *
+ * @return a retrieved value A
+ */
+ default A pos() {
+ return peeks(id());
+ }
+
+ /**
+ * Produce a new store by providing a relative change to the cursor.
+ *
+ * This can be implemented using the Comonad implementation as:
+ * return downcast(this.extend((Fn1<WA, A>) s -> s.peeks(f)));
+ * but is left unimplemented in the interface to prevent conflicts from using `seeks` to implement {@link Comonad#extendImpl}.
+ *
+ * @param f the relative change to make to the cursor before retrieving a value
+ * @return a new ComonadStore with updated cursor
+ */
+ ComonadStore seeks(Fn1 super S, ? extends S> f);
+
+ /**
+ * Produce a new store at a given cursor.
+ *
+ * @param s the cursor to extract the value from
+ * @return a new ComonadStore with updated cursor
+ */
+ default ComonadStore seek(S s) {
+ return seeks(constantly(s));
+ };
+
+ /**
+ * Retrieve a {@link Functor} of value(s) by perturbing the cursor.
+ *
+ * @param > f);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default A extract() {
+ return pos();
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/comonad/builtin/ComonadTraced.java b/src/main/java/com/jnape/palatable/lambda/comonad/builtin/ComonadTraced.java
new file mode 100644
index 000000000..886da414d
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/comonad/builtin/ComonadTraced.java
@@ -0,0 +1,37 @@
+package com.jnape.palatable.lambda.comonad.builtin;
+
+import com.jnape.palatable.lambda.comonad.Comonad;
+import com.jnape.palatable.lambda.functor.builtin.Traced;
+import com.jnape.palatable.lambda.monoid.Monoid;
+
+/**
+ * An interface for an Traced {@link Comonad}, which monoidally combines input to a trace function through a {@link Monoid} instance.
+ * A concrete class is provided in {@link Traced}.
+ *
+ * @param the type of the input values to the trace function
+ * @param the type of the output from the trace function
+ */
+public interface ComonadTraced, B, W extends Comonad, W>> extends Comonad {
+ /**
+ * Run a provided trace function.
+ *
+ * @param a the value to provide to the tracer.
+ * @return the value output by the trace function
+ */
+ B runTrace(A a);
+
+ /**
+ * Retrieve the {@link Monoid} instance for the input type A.
+ *
+ * @return a {@link Monoid} instance for A
+ */
+ Monoid getMonoid();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ default B extract() {
+ return runTrace(getMonoid().identity());
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java
index 3f2be1e92..cb4904d35 100644
--- a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java
+++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java
@@ -123,7 +123,7 @@ default
+ * {@code
+ * List
+ *
+ * @param the applied {@link Fn1 Fn1's} input type
+ * @param the applied {@link Fn1 Fn1's} output type
+ */
+public final class $ implements Fn2A to some {@link Comparable} type B and two values
@@ -16,6 +18,7 @@
* @param the value type
* @param the mapped comparison type
* @see CmpEq
+ * @see CmpEqWith
* @see LTBy
* @see GTBy
*/
@@ -28,7 +31,7 @@ private CmpEqBy() {
@Override
public Boolean checkedApply(Fn1 super A, ? extends B> compareFn, A x, A y) {
- return compareFn.apply(x).compareTo(compareFn.apply(y)) == 0;
+ return cmpEqWith(comparing(compareFn.toFunction()), x, y);
}
@Override
diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/CmpEqWith.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/CmpEqWith.java
new file mode 100644
index 000000000..ff95cd2fc
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/CmpEqWith.java
@@ -0,0 +1,62 @@
+package com.jnape.palatable.lambda.functions.builtin.fn3;
+
+import com.jnape.palatable.lambda.functions.Fn3;
+import com.jnape.palatable.lambda.functions.specialized.BiPredicate;
+import com.jnape.palatable.lambda.functions.specialized.Predicate;
+
+import java.util.Comparator;
+
+import static com.jnape.palatable.lambda.functions.builtin.fn3.Compare.compare;
+import static com.jnape.palatable.lambda.functions.ordering.ComparisonRelation.equal;
+import static com.jnape.palatable.lambda.functions.specialized.Predicate.predicate;
+
+/**
+ * Given a {@link Comparator} from some type A and two values of type A, return
+ * true if the first value is strictly equal to the second value (according to
+ * {@link Comparator#compare(Object, Object)} otherwise, return false.
+ *
+ * @param the value type
+ * @see CmpEqBy
+ * @see LTBy
+ * @see GTBy
+ * @see Compare
+ */
+public final class CmpEqWith implements Fn3A and two values of type A, return a
+ * {@link ComparisonRelation} of the first value with reference to the second value (according to
+ * {@link Comparator#compare(Object, Object)}. The order of parameters is flipped with respect to
+ * {@link Comparator#compare(Object, Object)} for more idiomatic partial application.
+ *
+ * {@code
+ * Compare.compare(naturalOrder(), 1, 2); // ComparisonRelation.GreaterThan
+ * Compare.compare(naturalOrder(), 2, 1); // ComparisonRelation.LessThan
+ * Compare.compare(naturalOrder(), 1, 1); // ComparisonRelation.Equal
+ * }
+ *
+ *
+ * @param the value type
+ * @see Comparator
+ * @see Compare
+ */
+public final class Compare implements Fn3A to some {@link Comparable} type B and two values
@@ -16,6 +18,7 @@
* @param the value type
* @param the mapped comparison type
* @see GT
+ * @see GTWith
* @see LTBy
*/
public final class GTBy> implements Fn3A to some {@link Comparable} type B and two values
@@ -18,6 +19,7 @@
* @param the value type
* @param the mapped comparison type
* @see GTE
+ * @see GTEWith
* @see LTEBy
*/
public final class GTEBy> implements Fn3A and two values of type A,
+ * return true if the second value is greater than or equal to the first value in
+ * terms of their mapped B results according to {@link Comparator#compare(Object, Object)};
+ * otherwise, return false.
+ *
+ * @param the value type
+ * @see GTE
+ * @see GTEBy
+ * @see LTEWith
+ */
+public final class GTEWith implements Fn3A and two values of type A,
+ * return true if the second value is strictly greater than the first value in
+ * terms of their mapped B results; otherwise, return false.
+ *
+ * @param the value type
+ * @see GT
+ * @see GTBy
+ * @see LTWith
+ */
+public final class GTWith implements Fn3A to some {@link Comparable} type B and two values
@@ -27,7 +29,7 @@ private LTBy() {
@Override
public Boolean checkedApply(Fn1 super A, ? extends B> compareFn, A y, A x) {
- return compareFn.apply(x).compareTo(compareFn.apply(y)) < 0;
+ return ltWith(comparing(compareFn.toFunction()), y, x);
}
@Override
diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEBy.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEBy.java
index bc755470d..4c377d583 100644
--- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEBy.java
+++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEBy.java
@@ -6,7 +6,8 @@
import com.jnape.palatable.lambda.functions.specialized.BiPredicate;
import com.jnape.palatable.lambda.functions.specialized.Predicate;
-import static com.jnape.palatable.lambda.functions.builtin.fn3.CmpEqBy.cmpEqBy;
+import static com.jnape.palatable.lambda.functions.builtin.fn3.LTEWith.lteWith;
+import static java.util.Comparator.comparing;
/**
* Given a mapping function from some type A to some {@link Comparable} type B and two values
@@ -28,7 +29,7 @@ private LTEBy() {
@Override
public Boolean checkedApply(Fn1 super A, ? extends B> compareFn, A y, A x) {
- return LTBy.ltBy(compareFn).or(cmpEqBy(compareFn)).apply(y, x);
+ return lteWith(comparing(compareFn.toFunction()), y, x);
}
@Override
diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEWith.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEWith.java
new file mode 100644
index 000000000..403f5c15f
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/LTEWith.java
@@ -0,0 +1,62 @@
+package com.jnape.palatable.lambda.functions.builtin.fn3;
+
+import com.jnape.palatable.lambda.functions.Fn3;
+import com.jnape.palatable.lambda.functions.builtin.fn2.LTE;
+import com.jnape.palatable.lambda.functions.specialized.BiPredicate;
+import com.jnape.palatable.lambda.functions.specialized.Predicate;
+
+import java.util.Comparator;
+
+import static com.jnape.palatable.lambda.functions.builtin.fn3.GTWith.gtWith;
+import static com.jnape.palatable.lambda.functions.specialized.Predicate.predicate;
+
+/**
+ * Given a {@link Comparator} from some type A and two values of type A,
+ * return true if the second value is less than or equal to the first value in
+ * terms of their mapped B results according to {@link Comparator#compare(Object, Object)};
+ * otherwise, return false.
+ *
+ * @param the value type
+ * @see LTE
+ * @see LTEBy
+ * @see GTEWith
+ */
+public final class LTEWith implements Fn3A and two values of type A,
+ * return true if the second value is strictly less than than the first value in
+ * terms of their mapped B results; otherwise, return false.
+ *
+ * @param the value type
+ * @see LT
+ * @see LTBy
+ * @see GTWith
+ */
+public final class LTWith implements Fn3{@link Fn1}<WA, B>.
+ * This can be thought of as a fixed, portable {@link Comonad#extend(Fn1)}.
+ *
+ * @param the extraction value of the input {@link Comonad}
+ * @param the store type
+ * @param the retrieved value type
+ */
+public final class Store implements Comonad>, ComonadStore> {
+ private final Fn1 super S, ? extends A> storage;
+ private final S cursor;
+
+ private Store(Fn1 super S, ? extends A> f, S s) {
+ this.storage = f;
+ this.cursor = s;
+ }
+
+ /**
+ * Constructor function for Store
+ *
+ * @param f the lookup function from storage to a retrieved value
+ * @param s the current cursor for looking up a value from f
+ * @param the type of the storage
+ * @param the type of the retrieved value
+ * @return a new instance of Store<S, A>
+ */
+ public static Store store(Fn1 super S, ? extends A> f, S s) {
+ return new Store<>(f, s);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final A peeks(Fn1 super S, ? extends S> f) {
+ return storage.contraMap(f).apply(cursor);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final Store seeks(Fn1 super S, ? extends S> f) {
+ return store(storage, f.apply(cursor));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public > f) {
+ return f.apply(cursor).fmap(c -> peeks(constantly(c)));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Store fmap(Fn1 super A, ? extends B> fn) {
+ return ComonadStore.super.fmap(fn).coerce();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Comonad> extendImpl(Fn1 super Comonad>, ? extends B> f) {
+ return store(s -> f.apply(store(storage, s)), cursor);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public >> Store extend(Fn1 super WA, ? extends B> f) {
+ return ComonadStore.super.extend(f).coerce();
+ }
+}
diff --git a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Traced.java b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Traced.java
new file mode 100644
index 000000000..a18d31803
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Traced.java
@@ -0,0 +1,85 @@
+package com.jnape.palatable.lambda.functor.builtin;
+
+import com.jnape.palatable.lambda.comonad.Comonad;
+import com.jnape.palatable.lambda.comonad.builtin.ComonadTraced;
+import com.jnape.palatable.lambda.functions.Fn1;
+import com.jnape.palatable.lambda.monoid.Monoid;
+
+/**
+ * A concrete implementation of the {@link ComonadTraced} interface.
+ *
+ * @param the type of the input to the {@link Traced#trace} function
+ * @param