Skip to content

Commit bbb97ae

Browse files
committed
Prism is a Monad; ProtoOptic offers implicit Profunctor/Identity optic
1 parent a929f92 commit bbb97ae

File tree

3 files changed

+119
-19
lines changed

3 files changed

+119
-19
lines changed

src/main/java/com/jnape/palatable/lambda/optics/Prism.java

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,27 @@
77
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
88
import com.jnape.palatable.lambda.functions.Fn1;
99
import com.jnape.palatable.lambda.functions.specialized.Pure;
10+
import com.jnape.palatable.lambda.functor.Applicative;
1011
import com.jnape.palatable.lambda.functor.Cocartesian;
1112
import com.jnape.palatable.lambda.functor.Functor;
1213
import com.jnape.palatable.lambda.functor.Profunctor;
1314
import com.jnape.palatable.lambda.functor.builtin.Identity;
15+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
1416
import com.jnape.palatable.lambda.functor.builtin.Market;
17+
import com.jnape.palatable.lambda.monad.Monad;
1518
import com.jnape.palatable.lambda.optics.functions.Matching;
1619
import com.jnape.palatable.lambda.optics.functions.Pre;
1720
import com.jnape.palatable.lambda.optics.functions.Re;
1821
import com.jnape.palatable.lambda.optics.functions.View;
1922

23+
import static com.jnape.palatable.lambda.adt.Either.left;
24+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
25+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Downcast.downcast;
26+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Upcast.upcast;
27+
import static com.jnape.palatable.lambda.optics.Prism.Simple.adapt;
28+
import static com.jnape.palatable.lambda.optics.functions.Matching.matching;
29+
import static com.jnape.palatable.lambda.optics.functions.Re.re;
30+
2031
/**
2132
* Prisms are {@link Iso Isos} that can fail in one direction. Example:
2233
* <pre>
@@ -42,10 +53,23 @@
4253
* @param <B> the input that guarantees its output
4354
*/
4455
@FunctionalInterface
45-
public interface Prism<S, T, A, B> extends
46-
ProtoOptic<Cocartesian<?, ?, ?>, S, T, A, B>,
47-
Optic<Cocartesian<?, ?, ?>, Identity<?>, S, T, A, B> {
56+
public interface Prism<S, T, A, B> extends ProtoOptic<Cocartesian<?, ?, ?>, S, T, A, B>, Monad<T, Prism<S, ?, A, B>> {
4857

58+
/**
59+
* Recover the two mappings encapsulated by this {@link Prism} by sending it through a {@link Market}.
60+
*
61+
* @return a {@link Tuple2 tuple} of the two mappings encapsulated by this {@link Prism}
62+
*/
63+
default Tuple2<Fn1<? super B, ? extends T>, Fn1<? super S, ? extends Either<T, A>>> unPrism() {
64+
return Tuple2.fill(this.<Market<A, B, ?, ?>, Identity<?>, Identity<B>, Identity<T>,
65+
Market<A, B, A, Identity<B>>, Market<A, B, S, Identity<T>>>apply(
66+
new Market<>(Identity::new, Either::right)).fmap(Identity::runIdentity))
67+
.biMap(Market::bt, Market::sta);
68+
}
69+
70+
/**
71+
* {@inheritDoc}
72+
*/
4973
@Override
5074
default <CoP extends Profunctor<?, ?, ? extends Cocartesian<?, ?, ?>>,
5175
CoF extends Functor<?, ? extends Identity<?>>,
@@ -58,15 +82,62 @@ public interface Prism<S, T, A, B> extends
5882
}
5983

6084
/**
61-
* Recover the two mappings encapsulated by this {@link Prism} by sending it through a {@link Market}.
62-
*
63-
* @return a {@link Tuple2 tuple} of the two mappings encapsulated by this {@link Prism}
85+
* {@inheritDoc}
6486
*/
65-
default Tuple2<Fn1<? super B, ? extends T>, Fn1<? super S, ? extends Either<T, A>>> unPrism() {
66-
return Tuple2.fill(this.<Market<A, B, ?, ?>, Identity<?>, Identity<B>, Identity<T>,
67-
Market<A, B, A, Identity<B>>, Market<A, B, S, Identity<T>>>apply(
68-
new Market<>(Identity::new, Either::right)).fmap(Identity::runIdentity))
69-
.biMap(Market::bt, Market::sta);
87+
@Override
88+
default <U> Prism<S, U, A, B> pure(U u) {
89+
return prism(constantly(left(u)), constantly(u));
90+
}
91+
92+
/**
93+
* {@inheritDoc}
94+
*/
95+
@Override
96+
default <U> Prism<S, U, A, B> flatMap(Fn1<? super T, ? extends Monad<U, Prism<S, ?, A, B>>> f) {
97+
return unPrism().into((bt, seta) -> prism(
98+
s -> seta.apply(s).match(t -> matching(f.apply(t).<Prism<S, U, A, B>>coerce(), s), Either::right),
99+
b -> View.<B, B, U, U>view(re(f.apply(bt.apply(b)).coerce())).apply(b)));
100+
}
101+
102+
/**
103+
* {@inheritDoc}
104+
*/
105+
@Override
106+
default <U> Prism<S, U, A, B> fmap(Fn1<? super T, ? extends U> fn) {
107+
return Monad.super.<U>fmap(fn).coerce();
108+
}
109+
110+
/**
111+
* {@inheritDoc}
112+
*/
113+
@Override
114+
default <U> Prism<S, U, A, B> zip(Applicative<Fn1<? super T, ? extends U>, Prism<S, ?, A, B>> appFn) {
115+
return Monad.super.zip(appFn).coerce();
116+
}
117+
118+
/**
119+
* {@inheritDoc}
120+
*/
121+
@Override
122+
default <U> Lazy<Prism<S, U, A, B>> lazyZip(
123+
Lazy<? extends Applicative<Fn1<? super T, ? extends U>, Prism<S, ?, A, B>>> lazyAppFn) {
124+
return Monad.super.lazyZip(lazyAppFn).fmap(Monad<U, Prism<S, ?, A, B>>::coerce);
125+
}
126+
127+
/**
128+
* {@inheritDoc}
129+
*/
130+
@Override
131+
default <U> Prism<S, U, A, B> discardL(Applicative<U, Prism<S, ?, A, B>> appB) {
132+
return Monad.super.discardL(appB).coerce();
133+
}
134+
135+
/**
136+
* {@inheritDoc}
137+
*/
138+
@Override
139+
default <U> Prism<S, T, A, B> discardR(Applicative<U, Prism<S, ?, A, B>> appB) {
140+
return Monad.super.discardR(appB).coerce();
70141
}
71142

72143
/**
@@ -85,8 +156,7 @@ static <S, T, A, B> Prism<S, T, A, B> prism(Fn1<? super S, ? extends CoProduct2<
85156
Fn1<? super B, ? extends T> bt) {
86157
return new Prism<S, T, A, B>() {
87158
@Override
88-
public <F extends Functor<?, ? extends F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(
89-
Pure<? extends F> pure) {
159+
public <F extends Applicative<?, F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(Pure<F> pure) {
90160
return Optic.<Cocartesian<?, ?, ?>,
91161
F,
92162
S, T, A, B,
@@ -113,8 +183,7 @@ static <S, T, A, B> Prism<S, T, A, B> prism(Fn1<? super S, ? extends CoProduct2<
113183
static <S, T, A, B> Prism<S, T, A, B> prism(ProtoOptic<? super Cocartesian<?, ?, ?>, S, T, A, B> protoOptic) {
114184
return new Prism<S, T, A, B>() {
115185
@Override
116-
public <F extends Functor<?, ? extends F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(
117-
Pure<? extends F> pure) {
186+
public <F extends Applicative<?, F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(Pure<F> pure) {
118187
Optic<? super Cocartesian<?, ?, ?>, F, S, T, A, B> optic = protoOptic.toOptic(pure);
119188
return Optic.reframe(optic);
120189
}
@@ -138,8 +207,7 @@ static <S, T, A, B> Prism<S, T, A, B> prism(
138207
Optic<? super Cocartesian<?, ?, ?>, ? super Functor<?, ?>, S, T, A, B> optic) {
139208
return new Prism<S, T, A, B>() {
140209
@Override
141-
public <F extends Functor<?, ? extends F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(
142-
Pure<? extends F> pure) {
210+
public <F extends Applicative<?, F>> Optic<Cocartesian<?, ?, ?>, F, S, T, A, B> toOptic(Pure<F> pure) {
143211
return Optic.reframe(optic);
144212
}
145213
};
@@ -160,6 +228,12 @@ static <S, A> Prism.Simple<S, A> simplePrism(Fn1<? super S, ? extends Maybe<A>>
160228
return Prism.<S, S, A, A>prism(s -> sMaybeA.apply(s).toEither(() -> s), as)::toOptic;
161229
}
162230

231+
232+
static <S, A> Prism.Simple<S, A> fromPartial(Fn1<? super S, ? extends A> partialSa,
233+
Fn1<? super A, ? extends S> as) {
234+
return adapt(prism(partialSa.<S, A>diMap(downcast(), upcast()).choose(), as));
235+
}
236+
163237
/**
164238
* A convenience type with a simplified type signature for common {@link Prism prism} with unified <code>S/T</code>
165239
* and <code>A/B</code> types.

src/main/java/com/jnape/palatable/lambda/optics/ProtoOptic.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.jnape.palatable.lambda.optics;
22

33
import com.jnape.palatable.lambda.functions.specialized.Pure;
4+
import com.jnape.palatable.lambda.functor.Applicative;
45
import com.jnape.palatable.lambda.functor.Functor;
56
import com.jnape.palatable.lambda.functor.Profunctor;
7+
import com.jnape.palatable.lambda.functor.builtin.Identity;
68

79
/**
810
* A generic supertype representation for a profunctor {@link Optic} that requires a {@link Pure} implementation to
@@ -14,7 +16,9 @@
1416
* @param <A> the left side of the input profunctor
1517
* @param <B> the right side's functor embedding of the input profunctor
1618
*/
17-
public interface ProtoOptic<P extends Profunctor<?, ?, ? extends P>, S, T, A, B> {
19+
@FunctionalInterface
20+
public interface ProtoOptic<P extends Profunctor<?, ?, ? extends P>, S, T, A, B>
21+
extends Optic<P, Identity<?>, S, T, A, B> {
1822

1923
/**
2024
* Given a {@link Pure} lifting function, fix this {@link ProtoOptic} to the given {@link Functor} and promote it to
@@ -24,5 +28,13 @@ public interface ProtoOptic<P extends Profunctor<?, ?, ? extends P>, S, T, A, B>
2428
* @param <F> the {@link Functor} bound
2529
* @return the {@link Optic}
2630
*/
27-
<F extends Functor<?, ? extends F>> Optic<P, F, S, T, A, B> toOptic(Pure<? extends F> pure);
31+
<F extends Applicative<?, F>> Optic<P, F, S, T, A, B> toOptic(Pure<F> pure);
32+
33+
@Override
34+
default <CoP extends Profunctor<?, ?, ? extends P>, CoF extends Functor<?, ? extends Identity<?>>,
35+
FB extends Functor<B, ? extends CoF>, FT extends Functor<T, ? extends CoF>,
36+
PAFB extends Profunctor<A, FB, ? extends CoP>,
37+
PSFT extends Profunctor<S, FT, ? extends CoP>> PSFT apply(PAFB pafb) {
38+
return toOptic(Pure.<Identity<?>>pure(Identity::new)).apply(pafb);
39+
}
2840
}

src/test/java/com/jnape/palatable/lambda/optics/PrismTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
import com.jnape.palatable.lambda.adt.Either;
44
import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
55
import com.jnape.palatable.lambda.functions.Fn1;
6+
import com.jnape.palatable.traitor.annotations.TestTraits;
7+
import com.jnape.palatable.traitor.runners.Traits;
68
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import testsupport.EquatableM;
11+
import testsupport.traits.ApplicativeLaws;
12+
import testsupport.traits.FunctorLaws;
13+
import testsupport.traits.MonadLaws;
714

815
import static com.jnape.palatable.lambda.adt.Either.left;
916
import static com.jnape.palatable.lambda.adt.Either.right;
@@ -16,10 +23,17 @@
1623
import static com.jnape.palatable.lambda.optics.functions.View.view;
1724
import static org.junit.Assert.assertEquals;
1825

26+
@RunWith(Traits.class)
1927
public class PrismTest {
2028

2129
private static final Fn1<String, Integer> PARSE_INT = Fn1.fn1(Integer::parseInt);
2230

31+
@TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class})
32+
public EquatableM<Prism<String, ?, Integer, Integer>, String> testSubject() {
33+
return new EquatableM<>(Prism.fromPartial(Integer::parseInt, Object::toString),
34+
prism -> matching(prism, "foo"));
35+
}
36+
2337
@Test
2438
public void prismLaws() {
2539
Prism<String, String, Integer, Integer> prism = prism(PARSE_INT.choose(), Object::toString);

0 commit comments

Comments
 (0)