Skip to content

Commit 7ee7a49

Browse files
committed
Implement either catamorphism with method dispatch
avoiding creation of Left/Right projections. Also some minor cleanup and refactoring to make more use of catamorphism.
1 parent 794f9b9 commit 7ee7a49

File tree

1 file changed

+59
-54
lines changed

1 file changed

+59
-54
lines changed

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

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
package fj.data;
22

3-
import static fj.Bottom.error;
3+
import fj.Equal;
4+
import fj.F;
5+
import fj.F0;
6+
import fj.Function;
7+
import fj.Hash;
8+
import fj.P1;
9+
import fj.Show;
10+
import fj.Unit;
11+
import fj.function.Effect1;
412

5-
import fj.*;
13+
import java.util.Collection;
14+
import java.util.Iterator;
615

16+
import static fj.Bottom.error;
17+
import static fj.Function.compose;
718
import static fj.Function.identity;
819
import static fj.P.p;
9-
10-
import fj.function.Effect1;
11-
1220
import static fj.Unit.unit;
1321
import static fj.data.Array.mkArray;
22+
import static fj.data.List.cons_;
1423
import static fj.data.List.list;
1524
import static fj.data.List.single;
16-
import static fj.data.List.cons_;
1725
import static fj.data.Option.some;
1826

19-
import java.util.Collection;
20-
import java.util.Iterator;
21-
2227
/**
2328
* The <code>Either</code> type represents a value of one of two possible types (a disjoint union).
2429
* The data constructors; <code>Left</code> and <code>Right</code> represent the two possible
@@ -72,11 +77,7 @@ public final RightProjection<A, B> right() {
7277
* @param right The function to call if this is right.
7378
* @return The reduced value.
7479
*/
75-
public final <X> X either(final F<A, X> left, final F<B, X> right) {
76-
return isLeft() ?
77-
left.f(left().value()) :
78-
right.f(right().value());
79-
}
80+
public abstract <X> X either(final F<A, X> left, final F<B, X> right);
8081

8182
/**
8283
* Map the given functions across the appropriate side.
@@ -86,9 +87,7 @@ public final <X> X either(final F<A, X> left, final F<B, X> right) {
8687
* @return A new either value after mapping with the appropriate function applied.
8788
*/
8889
public final <X, Y> Either<X, Y> bimap(final F<A, X> left, final F<B, Y> right) {
89-
return isLeft() ?
90-
left(left.f(left().value())) :
91-
right(right.f(right().value()));
90+
return either(compose(left_(), left), compose(right_(), right));
9291
}
9392

9493
@Override
@@ -107,7 +106,7 @@ public final int hashCode() {
107106
* @return The value of this either swapped to the opposing side.
108107
*/
109108
public final Either<B, A> swap() {
110-
return isLeft() ? new Right<>(((Left<A, B>) this).a) : new Left<>(((Right<A, B>) this).b);
109+
return either(right_(), left_());
111110
}
112111

113112
private static final class Left<A, B> extends Either<A, B> {
@@ -124,6 +123,11 @@ public boolean isLeft() {
124123
public boolean isRight() {
125124
return false;
126125
}
126+
127+
@Override
128+
public <X> X either(F<A, X> left, F<B, X> right) {
129+
return left.f(a);
130+
}
127131
}
128132

129133
private static final class Right<A, B> extends Either<A, B> {
@@ -140,6 +144,11 @@ public boolean isLeft() {
140144
public boolean isRight() {
141145
return true;
142146
}
147+
148+
@Override
149+
public <X> X either(F<A, X> left, F<B, X> right) {
150+
return right.f(b);
151+
}
143152
}
144153

145154
/**
@@ -414,17 +423,17 @@ public Collection<A> toCollection() {
414423
return toList().toCollection();
415424
}
416425

417-
public <C> Option<Either<C,B>> traverseOption(F<A, Option<C>> f) {
426+
public <C> Option<Either<C,B>> traverseOption(F<A, Option<C>> f) {
418427
return e.isLeft() ?
419-
f.f(value()).map(Either::<C, B>left) :
420-
some(Either.right(e.right().value()));
421-
}
428+
f.f(value()).map(left_()) :
429+
some(right(e.right().value()));
430+
}
422431

423-
public <C> Stream<Either<C, B>> traverseStream(F<A, Stream<C>> f) {
424-
return e.isLeft() ?
425-
f.f(value()).map(Either::<C, B>left) :
426-
Stream.single(Either.right(e.right().value()));
427-
}
432+
public <C> Stream<Either<C, B>> traverseStream(F<A, Stream<C>> f) {
433+
return e.isLeft() ?
434+
f.f(value()).map(left_()) :
435+
Stream.single(right(e.right().value()));
436+
}
428437
}
429438

430439
/**
@@ -543,7 +552,6 @@ public <X> Either<A, X> bind(final F<B, Either<A, X>> f) {
543552
return e.isRight() ? f.f(value()) : new Left<>(e.left().value());
544553
}
545554

546-
547555
/**
548556
* Anonymous bind through this projection.
549557
*
@@ -553,17 +561,18 @@ public <X> Either<A, X> bind(final F<B, Either<A, X>> f) {
553561
public <X> Either<A, X> sequence(final Either<A, X> e) {
554562
return bind(Function.constant(e));
555563
}
564+
556565
/**
557-
* Traverse with function that produces List (non-determinism).
558-
*
559-
* @param f the function to traverse with
560-
* @return An either after traversing through this projection.
561-
*/
562-
public <C> List<Either<A, C>> traverseList(final F<B, List<C>> f) {
563-
return e.isRight() ?
564-
f.f(value()).map(Either::right) :
565-
list(left(e.left().value()));
566-
}
566+
* Traverse with function that produces List (non-determinism).
567+
*
568+
* @param f the function to traverse with
569+
* @return An either after traversing through this projection.
570+
*/
571+
public <C> List<Either<A, C>> traverseList(final F<B, List<C>> f) {
572+
return e.isRight() ?
573+
f.f(value()).map(right_()) :
574+
list(left(e.left().value()));
575+
}
567576

568577
/**
569578
* Traverse with a function that has IO effect
@@ -573,19 +582,19 @@ public <C> List<Either<A, C>> traverseList(final F<B, List<C>> f) {
573582
*/
574583
public <C> IO<Either<A, C>> traverseIO(final F<B, IO<C>> f) {
575584
return e.isRight() ?
576-
IOFunctions.map(f.f(value()), Either::<A, C>right) :
585+
IOFunctions.map(f.f(value()), right_()) :
577586
IOFunctions.lazy(() -> left(e.left().value()));
578587
}
579588

580589
public <C> P1<Either<A, C>> traverseP1(final F<B, P1<C>> f) {
581590
return e.isRight() ?
582-
f.f(value()).map(Either::<A, C>right) :
591+
f.f(value()).map(right_()) :
583592
p(left(e.left().value()));
584593
}
585594

586595
public <C> Option<Either<A, C>> traverseOption(final F<B, Option<C>> f) {
587596
return e.isRight() ?
588-
f.f(value()).map(Either::<A, C>right) :
597+
f.f(value()).map(right_()) :
589598
some(left(e.left().value()));
590599
}
591600

@@ -758,8 +767,7 @@ public static <A, B, X> F<F<B, X>, F<Either<A, B>, Either<A, X>>> rightMap_() {
758767
* @return An either after joining.
759768
*/
760769
public static <A, B> Either<A, B> joinLeft(final Either<Either<A, B>, B> e) {
761-
final F<Either<A, B>, Either<A, B>> id = identity();
762-
return e.left().bind(id);
770+
return e.left().bind(identity());
763771
}
764772

765773
/**
@@ -769,8 +777,7 @@ public static <A, B> Either<A, B> joinLeft(final Either<Either<A, B>, B> e) {
769777
* @return An either after joining.
770778
*/
771779
public static <A, B> Either<A, B> joinRight(final Either<A, Either<A, B>> e) {
772-
final F<Either<A, B>, Either<A, B>> id = identity();
773-
return e.right().bind(id);
780+
return e.right().bind(identity());
774781
}
775782

776783
/**
@@ -781,7 +788,7 @@ public static <A, B> Either<A, B> joinRight(final Either<A, Either<A, B>> e) {
781788
*/
782789
public static <A, X> Either<List<A>, X> sequenceLeft(final List<Either<A, X>> a) {
783790
return a.isEmpty() ?
784-
Either.left(List.nil()) :
791+
left(List.nil()) :
785792
a.head().left().bind(aa -> sequenceLeft(a.tail()).left().map(cons_(aa)));
786793
}
787794

@@ -793,7 +800,7 @@ public static <A, X> Either<List<A>, X> sequenceLeft(final List<Either<A, X>> a)
793800
*/
794801
public static <B, X> Either<X, List<B>> sequenceRight(final List<Either<X, B>> a) {
795802
return a.isEmpty() ?
796-
Either.right(List.nil()) :
803+
right(List.nil()) :
797804
a.head().right().bind(bb -> sequenceRight(a.tail()).right().map(cons_(bb)));
798805
}
799806

@@ -807,10 +814,10 @@ public final <C> List<Either<A, C>> traverseListRight(final F<B, List<C>> f) {
807814
}
808815

809816
/**
810-
* Traversable instance of LeftProjection of Either for List.
811-
*
812-
* @return traversed value
813-
*/
817+
* Traversable instance of LeftProjection of Either for List.
818+
*
819+
* @return traversed value
820+
*/
814821
public final <C> List<Either<C, B>> traverseListLeft(final F<A, List<C>> f) {
815822
return left().traverseList(f);
816823
}
@@ -869,16 +876,14 @@ public final <C> Stream<Either<C, B>> traverseStreamLeft(final F<A, Stream<C>> f
869876
return left().traverseStream(f);
870877
}
871878

872-
873-
874879
/**
875880
* Takes an <code>Either</code> to its contained value within left or right.
876881
*
877882
* @param e The either to reduce.
878883
* @return An <code>Either</code> to its contained value within left or right.
879884
*/
880885
public static <A> A reduce(final Either<A, A> e) {
881-
return e.isLeft() ? e.left().value() : e.right().value();
886+
return e.either(identity(), identity());
882887
}
883888

884889
/**

0 commit comments

Comments
 (0)