Skip to content

Commit 848e2c0

Browse files
authored
Merge pull request #405 from drewctaylor/functionaljava-403-validation
Fixes #403. Add fj.data.Validation.sequence*, fj.data.Validation.traverse*.
2 parents d89c845 + b8c3093 commit 848e2c0

2 files changed

Lines changed: 530 additions & 37 deletions

File tree

core/src/main/java/fj/data/Validation.java

Lines changed: 321 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package fj.data;
22

33
import fj.*;
4+
import fj.control.Trampoline;
45
import fj.function.Effect1;
56

6-
import static fj.Function.curry;
7-
import static fj.P.p;
7+
import java.util.Iterator;
88

9-
import static fj.Unit.unit;
109
import static fj.Bottom.error;
10+
import static fj.Function.*;
11+
import static fj.P.p;
12+
import static fj.Unit.unit;
13+
import static fj.data.Either.*;
1114
import static fj.data.List.list;
1215

13-
import java.util.Iterator;
14-
1516
/**
1617
* Isomorphic to {@link Either} but has renamed functions and represents failure on the left and success on the right.
1718
* This type also has accumulating functions that accept a {@link Semigroup} for binding computation while keeping error
@@ -825,44 +826,327 @@ public static <A, E> Validation<List<E>, List<A>> sequenceNonCumulative(List<Val
825826
}
826827
}
827828

828-
public final <C> List<Validation<E, C>> traverseList(F<T, List<C>> f){
829-
return isSuccess() ?
830-
f.f(success()).map(Validation::success) :
831-
List.iterableList(fail(e.left().value()));
832-
}
829+
/**
830+
* Sequence the given validation and collect the output on the left side of an either.
831+
*
832+
* @param validation the given validation
833+
* @param <E> the type of the failure value
834+
* @param <R> the type of the right value
835+
* @param <C> the type of the left value
836+
* @return the either
837+
*/
838+
public static final <E, R, C> Either<Validation<E, C>, R> sequenceEitherLeft(final Validation<E, Either<C, R>> validation) {
839+
return validation.traverseEitherLeft(identity());
840+
}
833841

834-
public final <C> Stream<Validation<E, C>> traverseStream(F<T, Stream<C>> f){
835-
return isSuccess() ?
836-
f.f(success()).map(Validation::success) :
837-
Stream.iterableStream(fail(e.left().value()));
838-
}
842+
/**
843+
* Sequence the given validation and collect the output on the right side of an either.
844+
*
845+
* @param validation the given validation
846+
* @param <E> the type of the failure value
847+
* @param <C> the type of the right value
848+
* @param <L> the type of the left value
849+
* @return the either
850+
*/
851+
public static final <E, L, C> Either<L, Validation<E, C>> sequenceEitherRight(final Validation<E, Either<L, C>> validation) {
852+
return validation.traverseEitherRight(identity());
853+
}
839854

840-
public final <C> Option<Validation<E, C>> traverseOption(F<T, Option<C>> f){
841-
return isSuccess() ?
842-
f.f(success()).map(Validation::success) :
843-
Option.some(fail(e.left().value()));
844-
}
855+
/**
856+
* Sequence the given validation and collect the output as a function.
857+
*
858+
* @param validation the given validation
859+
* @param <E> the type of the failure value
860+
* @param <C> the type of input value
861+
* @param <B> the type of output value
862+
* @return the function
863+
*/
864+
public static final <E, C, B> F<C, Validation<E, B>> sequenceF(final Validation<E, F<C, B>> validation) {
865+
return validation.traverseF(identity());
866+
}
845867

846-
public final <C> IO<Validation<E, C>> traverseIO(F<T, IO<C>> f){
847-
return isSuccess() ?
848-
IOFunctions.map(f.f(success()), Validation::success) :
849-
IOFunctions.unit(fail(e.left().value()));
850-
}
868+
/**
869+
* Sequence the given validation and collect the output as an IO.
870+
*
871+
* @param validation the given validation
872+
* @param <E> the type of the failure value
873+
* @param <C> the type of the IO value
874+
* @return the IO
875+
*/
876+
public static final <E, C> IO<Validation<E, C>> sequenceIO(final Validation<E, IO<C>> validation) {
877+
return validation.traverseIO(identity());
878+
}
851879

852-
public final <C> P1<Validation<E, C>> traverseP1(F<T, P1<C>> f){
853-
return isSuccess() ?
854-
f.f(success()).map(Validation::success) :
855-
p(fail(e.left().value()));
856-
}
880+
/**
881+
* Sequence the given validation and collect the output as a list.
882+
*
883+
* @param validation the given validation
884+
* @param <E> the type of the failure value
885+
* @param <C> the type of the list value
886+
* @return the list
887+
*/
888+
public static final <E, C> List<Validation<E, C>> sequenceList(final Validation<E, List<C>> validation) {
889+
return validation.traverseList(identity());
890+
}
891+
892+
/**
893+
* Sequence the given validation and collect the output as an option.
894+
*
895+
* @param validation the given validation
896+
* @param <E> the type of the failure value
897+
* @param <C> the type of the option value
898+
* @return the option
899+
*/
900+
public static final <E, C> Option<Validation<E, C>> sequenceOption(final Validation<E, Option<C>> validation) {
901+
return validation.traverseOption(identity());
902+
}
857903

904+
/**
905+
* Sequence the given validation and collect the output as a P1.
906+
*
907+
* @param validation the given validation
908+
* @param <E> the type of the failure value
909+
* @param <C> the type of the P1 value
910+
* @return the P1
911+
*/
912+
public static final <E, C> P1<Validation<E, C>> sequenceP1(final Validation<E, P1<C>> validation) {
913+
return validation.traverseP1(identity());
914+
}
858915

859-
public static <A, E> List<E> fails(List<Validation<E, ?>> list) {
860-
return list.filter(Validation::isFail).map(v -> v.fail());
861-
}
916+
/**
917+
* Sequence the given validation and collect the output as a seq.
918+
*
919+
* @param validation the given validation
920+
* @param <E> the type of the failure value
921+
* @param <C> the type of the seq value
922+
* @return the seq
923+
*/
924+
public static final <E, C> Seq<Validation<E, C>> sequenceSeq(final Validation<E, Seq<C>> validation) {
925+
return validation.traverseSeq(identity());
926+
}
862927

863-
public static <A, E> List<A> successes(List<Validation<?, A>> list) {
864-
return list.filter(Validation::isSuccess).map(v -> v.success());
865-
}
928+
/**
929+
* Sequence the given validation and collect the output as a set.
930+
*
931+
* @param ordE the given failure value ord
932+
* @param ordC the given success value ord
933+
* @param validation the given validation
934+
* @param <E> the type of the failure value
935+
* @param <C> the type of the set value
936+
* @return the set
937+
*/
938+
public static final <E, C> Set<Validation<E, C>> sequenceSet(final Ord<E> ordE, final Ord<C> ordC, final Validation<E, Set<C>> validation) {
939+
return validation.traverseSet(ordE, ordC, identity());
940+
}
941+
942+
/**
943+
* Sequence the given validation and collect the output as a stream.
944+
*
945+
* @param validation the given validation
946+
* @param <E> the type of the failure value
947+
* @param <C> the type of the stream value
948+
* @return the stream
949+
*/
950+
public static final <E, C> Stream<Validation<E, C>> sequenceStream(final Validation<E, Stream<C>> validation) {
951+
return validation.traverseStream(identity());
952+
}
953+
954+
/**
955+
* Sequence the given validation and collect the output as a trampoline.
956+
*
957+
* @param validation the given validation
958+
* @param <E> the type of the failure value
959+
* @param <C> the type of the trampoline value
960+
* @return the trampoline
961+
*/
962+
public static final <E, C> Trampoline<Validation<E, C>> sequenceTrampoline(final Validation<E, Trampoline<C>> validation) {
963+
return validation.traverseTrampoline(identity());
964+
}
965+
966+
/**
967+
* Sequence the given validation and collect the output as a validation.
968+
*
969+
* @param validation the given validation
970+
* @param <E> the type of the failure value
971+
* @param <E1> the type of the failure value
972+
* @param <C> the type of the success value
973+
* @return the validation
974+
*/
975+
public static final <E, E1, C> Validation<E1, Validation<E, C>> sequenceValidation(final Validation<E, Validation<E1, C>> validation) {
976+
return validation.traverseValidation(identity());
977+
}
978+
979+
/**
980+
* Traverse this validation with the given function and collect the output on the left side of an either.
981+
*
982+
* @param f the given function
983+
* @param <C> the type of the left value
984+
* @param <R> the type of the right value
985+
* @return the list
986+
*/
987+
public final <R, C> Either<Validation<E, C>, R> traverseEitherLeft(final F<T, Either<C, R>> f) {
988+
return validation(
989+
failure -> left(fail(failure)),
990+
success -> f.f(success).left().map(Validation::success));
991+
}
992+
993+
/**
994+
* Traverse this validation with the given function and collect the output on the right side of an either.
995+
*
996+
* @param f the given function
997+
* @param <L> the type of the left value
998+
* @param <C> the type of the right value
999+
* @return the list
1000+
*/
1001+
public final <L, C> Either<L, Validation<E, C>> traverseEitherRight(final F<T, Either<L, C>> f) {
1002+
return validation(
1003+
failure -> right(fail(failure)),
1004+
success -> f.f(success).right().map(Validation::success));
1005+
}
1006+
1007+
/**
1008+
* Traverse this validation with the given function and collect the output as a function.
1009+
*
1010+
* @param f the given function
1011+
* @param <C> the type of the input value
1012+
* @param <B> the type of the output value
1013+
* @return the function
1014+
*/
1015+
public final <C, B> F<C, Validation<E, B>> traverseF(final F<T, F<C, B>> f) {
1016+
return validation(
1017+
failure -> constant(fail(failure)),
1018+
success -> andThen(f.f(success), Validation::success));
1019+
}
1020+
1021+
/**
1022+
* Traverse this validation with the given function and collect the output as an IO.
1023+
*
1024+
* @param f the given function
1025+
* @param <C> the type of the IO value
1026+
* @return the IO
1027+
*/
1028+
public final <C> IO<Validation<E, C>> traverseIO(final F<T, IO<C>> f) {
1029+
return validation(
1030+
failure -> IOFunctions.unit(fail(failure)),
1031+
success -> IOFunctions.map(f.f(success), Validation::success));
1032+
}
1033+
1034+
/**
1035+
* Traverse this validation with the given function and collect the output as a list.
1036+
*
1037+
* @param f the given function
1038+
* @param <C> the type of the list value
1039+
* @return the list
1040+
*/
1041+
public final <C> List<Validation<E, C>> traverseList(final F<T, List<C>> f) {
1042+
return validation(
1043+
failure -> List.single(fail(failure)),
1044+
success -> f.f(success).map(Validation::success));
1045+
}
1046+
1047+
/**
1048+
* Traverse this validation with the given function and collect the output as an option.
1049+
*
1050+
* @param f the given function
1051+
* @param <C> the type of the option value
1052+
* @return the option
1053+
*/
1054+
public final <C> Option<Validation<E, C>> traverseOption(F<T, Option<C>> f) {
1055+
return validation(
1056+
failure -> Option.some(fail(failure)),
1057+
success -> f.f(success).map(Validation::success));
1058+
}
1059+
1060+
/**
1061+
* Traverse this validation with the given function and collect the output as a P1.
1062+
*
1063+
* @param f the given function
1064+
* @param <C> the type of the P1 value
1065+
* @return the P1
1066+
*/
1067+
public final <C> P1<Validation<E, C>> traverseP1(final F<T, P1<C>> f) {
1068+
return validation(
1069+
failure -> p(fail(failure)),
1070+
success -> f.f(success).map(Validation::success));
1071+
}
1072+
1073+
/**
1074+
* Traverse this validation with the given function and collect the output as a seq.
1075+
*
1076+
* @param f the given function
1077+
* @param <C> the type of the seq value
1078+
* @return the seq
1079+
*/
1080+
public final <C> Seq<Validation<E, C>> traverseSeq(final F<T, Seq<C>> f) {
1081+
return validation(
1082+
failure -> Seq.single(fail(failure)),
1083+
success -> f.f(success).map(Validation::success));
1084+
}
1085+
1086+
/**
1087+
* Traverse this validation with the given function and collect the output as a set; use the given success and failure value ords to order the set.
1088+
*
1089+
* @param ordE the given failure value ord
1090+
* @param ordC the given success value ord
1091+
* @param f the given function
1092+
* @param <C> the type of the set value
1093+
* @return the set
1094+
*/
1095+
public final <C> Set<Validation<E, C>> traverseSet(final Ord<E> ordE, final Ord<C> ordC, final F<T, Set<C>> f) {
1096+
final Ord<Validation<E, C>> ord = Ord.validationOrd(ordE, ordC);
1097+
return validation(
1098+
failure -> Set.single(ord, fail(failure)),
1099+
success -> f.f(success).map(ord, Validation::success));
1100+
}
1101+
1102+
/**
1103+
* Traverse this validation with the given function and collect the output as a stream.
1104+
*
1105+
* @param f the given function
1106+
* @param <C> the type of the stream value
1107+
* @return the stream
1108+
*/
1109+
public final <C> Stream<Validation<E, C>> traverseStream(final F<T, Stream<C>> f) {
1110+
return validation(
1111+
failure -> Stream.single(fail(failure)),
1112+
success -> f.f(success).map(Validation::success));
1113+
}
1114+
1115+
/**
1116+
* Traverse this validation with the given function and collect the output as a trampoline.
1117+
*
1118+
* @param f the given function
1119+
* @param <C> the type of the trampoline value
1120+
* @return the trampoline
1121+
*/
1122+
public final <C> Trampoline<Validation<E, C>> traverseTrampoline(final F<T, Trampoline<C>> f) {
1123+
return validation(
1124+
failure -> Trampoline.pure(fail(failure)),
1125+
success -> f.f(success).map(Validation::success));
1126+
}
1127+
1128+
/**
1129+
* Traverse this validation with the given function and collect the output as a validation.
1130+
*
1131+
* @param f the given function
1132+
* @param <E1> the type of the failure value
1133+
* @param <C> the type of the seq value
1134+
* @return the validation
1135+
*/
1136+
public final <E1, C> Validation<E1, Validation<E, C>> traverseValidation(final F<T, Validation<E1, C>> f) {
1137+
return validation(
1138+
failure -> success(fail(failure)),
1139+
success -> f.f(success).map(Validation::success));
1140+
}
1141+
1142+
1143+
public static <A, E> List<E> fails(List<Validation<E, ?>> list) {
1144+
return list.filter(Validation::isFail).map(v -> v.fail());
1145+
}
1146+
1147+
public static <A, E> List<A> successes(List<Validation<?, A>> list) {
1148+
return list.filter(Validation::isSuccess).map(v -> v.success());
1149+
}
8661150

8671151
/**
8681152
* A failing projection of a validation.
@@ -1268,7 +1552,7 @@ public static Validation<NumberFormatException, Short> parseShort(final String s
12681552
}
12691553

12701554
/**
1271-
* A function that parses a string into a short.
1555+
* A function that parses a string into a short.
12721556
*/
12731557
public static final F<String, Validation<NumberFormatException, Short>> parseShort = Validation::parseShort;
12741558

0 commit comments

Comments
 (0)