Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions core/src/main/java/fj/data/optic/Fold.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package fj.data.optic;

import fj.F;
import fj.Function;
import fj.Monoid;
import fj.data.Either;
import fj.data.List;
import fj.data.Option;

/**
* A {@link Fold} can be seen as a {@link Getter} with many targets or a weaker {@link PTraversal} which cannot modify its
* target.
*
* {@link Fold} is on the top of the Optic hierarchy which means that {@link Getter}, {@link PTraversal}, {@link POptional},
* {@link PLens}, {@link PPrism} and {@link PIso} are valid {@link Fold}
*
* @param <S> the source of a {@link Fold}
* @param <A> the target of a {@link Fold}
*/
public abstract class Fold<S, A> {

/**
* map each target to a {@link Monoid} and combine the results underlying representation of {@link Fold}, all {@link Fold}
* methods are defined in terms of foldMap
*/
public abstract <M> F<S, M> foldMap(Monoid<M> m, F<A, M> f);

/** combine all targets using a target's {@link Monoid} */
public final F<S, A> fold(final Monoid<A> m) {
return foldMap(m, Function.identity());
}

/**
* get all the targets of a {@link Fold} TODO: Shall it return a Stream as there might be an infinite number of targets?
*/
public final List<A> getAll(final S s) {
return foldMap(Monoid.listMonoid(), List::single).f(s);
}

/** find the first target of a {@link Fold} matching the predicate */
public final F<S, Option<A>> find(final F<A, Boolean> p) {
return foldMap(Monoid.optionMonoid(), a -> p.f(a) ? Option.some(a) : Option.none());
}

/** get the first target of a {@link Fold} */
public final Option<A> headOption(final S s) {
return find(__ -> true).f(s);
}

/** check if at least one target satisfies the predicate */
public final F<S, Boolean> exist(final F<A, Boolean> p) {
return foldMap(Monoid.disjunctionMonoid, p);
}

/** check if all targets satisfy the predicate */
public final F<S, Boolean> all(final F<A, Boolean> p) {
return foldMap(Monoid.conjunctionMonoid, p);
}

/** join two {@link Fold} with the same target */
public final <S1> Fold<Either<S, S1>, A> sum(final Fold<S1, A> other) {
return new Fold<Either<S, S1>, A>() {
@Override
public <B> F<Either<S, S1>, B> foldMap(final Monoid<B> m, final F<A, B> f) {
return s -> s.either(Fold.this.foldMap(m, f), other.foldMap(m, f));
}
};
}

/**********************************************************/
/** Compose methods between a {@link Fold} and another Optics */
/**********************************************************/

/** compose a {@link Fold} with a {@link Fold} */
public final <B> Fold<S, B> composeFold(final Fold<A, B> other) {
return new Fold<S, B>() {
@Override
public <C> F<S, C> foldMap(final Monoid<C> m, final F<B, C> f) {
return Fold.this.<C> foldMap(m, other.<C> foldMap(m, f));
}
};
}

/** compose a {@link Fold} with a {@link Getter} */
public final <C> Fold<S, C> composeGetter(final Getter<A, C> other) {
return composeFold(other.asFold());
}

/** compose a {@link Fold} with a {@link POptional} */
public final <B, C, D> Fold<S, C> composeOptional(final POptional<A, B, C, D> other) {
return composeFold(other.asFold());
}

/** compose a {@link Fold} with a {@link PPrism} */
public final <B, C, D> Fold<S, C> composePrism(final PPrism<A, B, C, D> other) {
return composeFold(other.asFold());
}

/** compose a {@link Fold} with a {@link PLens} */
public final <B, C, D> Fold<S, C> composeLens(final PLens<A, B, C, D> other) {
return composeFold(other.asFold());
}

/** compose a {@link Fold} with a {@link PIso} */
public final <B, C, D> Fold<S, C> composeIso(final PIso<A, B, C, D> other) {
return composeFold(other.asFold());
}

public static <A> Fold<A, A> id() {
return PIso.<A, A> pId().asFold();
}

public static final <A> Fold<Either<A, A>, A> codiagonal() {
return new Fold<Either<A, A>, A>() {
@Override
public <B> F<Either<A, A>, B> foldMap(final Monoid<B> m, final F<A, B> f) {
return e -> e.either(f, f);
}
};
}

}
110 changes: 110 additions & 0 deletions core/src/main/java/fj/data/optic/Getter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package fj.data.optic;

import fj.F;
import fj.Function;
import fj.Monoid;
import fj.P;
import fj.P2;
import fj.data.Either;

/**
* A {@link Getter} can be seen as a glorified get method between a type S and a type A.
*
* A {@link Getter} is also a valid {@link Fold}
*
* @param <S> the source of a {@link Getter}
* @param <A> the target of a {@link Getter}
*/
public abstract class Getter<S, A> {

Getter() {
super();
}

/** get the target of a {@link Getter} */
public abstract A get(S s);

/** join two {@link Getter} with the same target */
public final <S1> Getter<Either<S, S1>, A> sum(final Getter<S1, A> other) {
return getter(e -> e.either(this::get, other::get));
}

/** pair two disjoint {@link Getter} */
public final <S1, A1> Getter<P2<S, S1>, P2<A, A1>> product(final Getter<S1, A1> other) {
return getter(p2 -> P.p(this.get(p2._1()), other.get(p2._2())));
}

public final <B> Getter<P2<S, B>, P2<A, B>> first() {
return getter(p -> P.p(this.get(p._1()), p._2()));
}

public final <B> Getter<P2<B, S>, P2<B, A>> second() {
return getter(p -> P.p(p._1(), this.get(p._2())));
}

/*************************************************************/
/** Compose methods between a {@link Getter} and another Optics */
/*************************************************************/

/** compose a {@link Getter} with a {@link Fold} */
public final <B> Fold<S, B> composeFold(final Fold<A, B> other) {
return asFold().composeFold(other);
}

/** compose a {@link Getter} with a {@link Getter} */
public final <B> Getter<S, B> composeGetter(final Getter<A, B> other) {
return getter(s -> other.get(get(s)));
}

/** compose a {@link Getter} with a {@link POptional} */
public final <B, C, D> Fold<S, C> composeOptional(final POptional<A, B, C, D> other) {
return asFold().composeOptional(other);
}

/** compose a {@link Getter} with a {@link PPrism} */
public final <B, C, D> Fold<S, C> composePrism(final PPrism<A, B, C, D> other) {
return asFold().composePrism(other);
}

/** compose a {@link Getter} with a {@link PLens} */
public final <B, C, D> Getter<S, C> composeLens(final PLens<A, B, C, D> other) {
return composeGetter(other.asGetter());
}

/** compose a {@link Getter} with a {@link PIso} */
public final <B, C, D> Getter<S, C> composeIso(final PIso<A, B, C, D> other) {
return composeGetter(other.asGetter());
}

/******************************************************************/
/** Transformation methods to view a {@link Getter} as another Optics */
/******************************************************************/

/** view a {@link Getter} with a {@link Fold} */
public final Fold<S, A> asFold() {
return new Fold<S, A>() {
@Override
public <B> F<S, B> foldMap(final Monoid<B> m, final F<A, B> f) {
return s -> f.f(get(s));
}
};
}

public static <A> Getter<A, A> id() {
return PIso.<A, A> pId().asGetter();
}

public static final <A> Getter<Either<A, A>, A> codiagonal() {
return getter(e -> e.either(Function.identity(), Function.identity()));
}

public static final <S, A> Getter<S, A> getter(final F<S, A> get) {
return new Getter<S, A>() {

@Override
public A get(final S s) {
return get.f(s);
}
};
}
}
133 changes: 133 additions & 0 deletions core/src/main/java/fj/data/optic/Iso.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package fj.data.optic;

import fj.F;
import fj.P2;

/** {@link PIso} when S = T and A = B */
public final class Iso<S, A> extends PIso<S, S, A, A> {

final PIso<S, S, A, A> pIso;

public Iso(final PIso<S, S, A, A> pIso) {
this.pIso = pIso;
}

@Override
public A get(final S s) {
return pIso.get(s);
}

@Override
public S reverseGet(final A a) {
return pIso.reverseGet(a);
}

@Override
public Iso<A, S> reverse() {
return new Iso<>(pIso.reverse());
}

/** pair two disjoint {@link Iso} */
public <S1, A1> Iso<P2<S, S1>, P2<A, A1>> product(final Iso<S1, A1> other) {
return new Iso<>(pIso.product(other.pIso));
}

@Override
public <C> Iso<P2<S, C>, P2<A, C>> first() {
return new Iso<>(pIso.first());
}

@Override
public <C> Iso<P2<C, S>, P2<C, A>> second() {
return new Iso<>(pIso.second());
}

/**********************************************************/
/** Compose methods between an {@link Iso} and another Optics */
/**********************************************************/

/** compose an {@link Iso} with a {@link Setter} */
public final <C> Setter<S, C> composeSetter(final Setter<A, C> other) {
return new Setter<>(pIso.composeSetter(other.pSetter));
}

/** compose an {@link Iso} with a {@link Traversal} */
public final <C> Traversal<S, C> composeTraversal(final Traversal<A, C> other) {
return new Traversal<>(pIso.composeTraversal(other.pTraversal));
}

/** compose an {@link Iso} with a {@link Optional} */
public final <C> Optional<S, C> composeOptional(final Optional<A, C> other) {
return new Optional<>(pIso.composeOptional(other.pOptional));
}

/** compose an {@link Iso} with a {@link Prism} */
public final <C> Prism<S, C> composePrism(final Prism<A, C> other) {
return new Prism<>(pIso.composePrism(other.pPrism));
}

/** compose an {@link Iso} with a {@link Lens} */
public final <C> Lens<S, C> composeLens(final Lens<A, C> other) {
return asLens().composeLens(other);
}

/** compose an {@link Iso} with an {@link Iso} */
public final <C> Iso<S, C> composeIso(final Iso<A, C> other) {
return new Iso<>(pIso.composeIso(other.pIso));
}

/****************************************************************/
/** Transformation methods to view an {@link Iso} as another Optics */
/****************************************************************/

/** view an {@link Iso} as a {@link Setter} */
@Override
public final Setter<S, A> asSetter() {
return new Setter<>(pIso.asSetter());
}

/** view an {@link Iso} as a {@link Traversal} */
@Override
public final Traversal<S, A> asTraversal() {
return new Traversal<>(pIso.asTraversal());
}

/** view an {@link Iso} as a {@link Optional} */
@Override
public final Optional<S, A> asOptional() {
return new Optional<>(pIso.asOptional());
}

/** view an {@link Iso} as a {@link Prism} */
@Override
public final Prism<S, A> asPrism() {
return new Prism<>(pIso.asPrism());
}

/** view an {@link Iso} as a {@link Lens} */
@Override
public final Lens<S, A> asLens() {
return new Lens<>(pIso.asLens());
}

/** create an {@link Iso} using a pair of functions: one to get the target and one to get the source. */
public static <S, A> Iso<S, A> iso(final F<S, A> get, final F<A, S> reverseGet) {
return new Iso<>(PIso.pIso(get, reverseGet));
}

/**
* create an {@link Iso} between any type and itself. id is the zero element of optics composition, for all optics o of type O
* (e.g. Lens, Iso, Prism, ...):
*
* <pre>
* o composeIso Iso.id == o
* Iso.id composeO o == o
* </pre>
*
* (replace composeO by composeLens, composeIso, composePrism, ...)
*/
public static <S> Iso<S, S> id() {
return new Iso<>(PIso.pId());
}

}
Loading