Skip to content

Commit d7fac75

Browse files
committed
Adding ReaderT
1 parent 8c92260 commit d7fac75

3 files changed

Lines changed: 219 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
88
- `Optic#andThen`, `Optic#compose`, and other defaults added
99
- `Prism#andThen`, `Prism#compose` begets another `Prism`
1010
- `Prism#fromPartial` public interfaces
11+
- `ReaderT`, the transformer for the reader monad
1112

1213
## [4.0.0] - 2019-05-20
1314
### Changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.jnape.palatable.lambda.monad.transformer.builtin;
2+
3+
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
4+
import com.jnape.palatable.lambda.functions.Fn1;
5+
import com.jnape.palatable.lambda.functor.Applicative;
6+
import com.jnape.palatable.lambda.functor.Cartesian;
7+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
8+
import com.jnape.palatable.lambda.monad.Monad;
9+
import com.jnape.palatable.lambda.monad.transformer.MonadT;
10+
11+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
12+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Tupler2.tupler;
13+
14+
/**
15+
* A {@link MonadT monad transformer} for any {@link Fn1 function} from some type <code>R</code> to some
16+
* {@link Monad monadic} embedding <code>{@link Monad}&lt;A, M&gt;</code>.
17+
*
18+
* @param <R> the input type
19+
* @param <M> the returned {@link Monad}
20+
* @param <A> the embedded output type
21+
*/
22+
public interface ReaderT<R, M extends Monad<?, M>, A> extends
23+
MonadT<Fn1<R, ?>, M, A>,
24+
Cartesian<R, A, ReaderT<?, M, ?>> {
25+
26+
/**
27+
* Run the computation represented by this {@link ReaderT}.
28+
*
29+
* @param r the input
30+
* @return the {¬@link Monad monadic} embedding {@link Monad}&lt;A, M&gt;
31+
*/
32+
Monad<A, M> runReaderT(R r);
33+
34+
/**
35+
* Map the current {@link Monad monadic} embedding to a new one in a potentially different {@link Monad}.
36+
*
37+
* @param fn the mapping function
38+
* @param <MA> the inference target of the current {@link Monad monadic} embedding
39+
* @param <N> the new {@link Monad} to embed the result in
40+
* @param <B> the new embedded result
41+
* @return the mapped {@link ReaderT}
42+
*/
43+
default <MA extends Monad<A, M>, N extends Monad<?, N>, B> ReaderT<R, N, B> mapReaderT(
44+
Fn1<? super MA, ? extends Monad<B, N>> fn) {
45+
return readerT(r -> fn.apply(runReaderT(r).coerce()));
46+
}
47+
48+
/**
49+
* {@inheritDoc}
50+
*/
51+
@Override
52+
default <GA extends Monad<A, M>, FGA extends Monad<GA, Fn1<R, ?>>> FGA run() {
53+
return Fn1.<R, GA>fn1(r -> runReaderT(r).coerce()).coerce();
54+
}
55+
56+
/**
57+
* {@inheritDoc}
58+
*/
59+
@Override
60+
default <B> ReaderT<R, M, B> flatMap(Fn1<? super A, ? extends Monad<B, MonadT<Fn1<R, ?>, M, ?>>> f) {
61+
return readerT(r -> runReaderT(r).flatMap(a -> f.apply(a).<ReaderT<R, M, B>>coerce().runReaderT(r)));
62+
}
63+
64+
/**
65+
* {@inheritDoc}
66+
*/
67+
@Override
68+
default <B> ReaderT<R, M, B> pure(B b) {
69+
return readerT(r -> runReaderT(r).pure(b));
70+
}
71+
72+
/**
73+
* {@inheritDoc}
74+
*/
75+
@Override
76+
default <B> ReaderT<R, M, B> fmap(Fn1<? super A, ? extends B> fn) {
77+
return MonadT.super.<B>fmap(fn).coerce();
78+
}
79+
80+
/**
81+
* {@inheritDoc}
82+
*/
83+
@Override
84+
default <B> ReaderT<R, M, B> zip(Applicative<Fn1<? super A, ? extends B>, MonadT<Fn1<R, ?>, M, ?>> appFn) {
85+
return readerT(r -> runReaderT(r).zip(appFn.<ReaderT<R, M, Fn1<? super A, ? extends B>>>coerce().runReaderT(r)));
86+
}
87+
88+
/**
89+
* {@inheritDoc}
90+
*/
91+
@Override
92+
default <B> Lazy<? extends ReaderT<R, M, B>> lazyZip(
93+
Lazy<? extends Applicative<Fn1<? super A, ? extends B>, MonadT<Fn1<R, ?>, M, ?>>> lazyAppFn) {
94+
return lazyAppFn.fmap(this::zip);
95+
}
96+
97+
/**
98+
* {@inheritDoc}
99+
*/
100+
@Override
101+
default <B> ReaderT<R, M, B> discardL(Applicative<B, MonadT<Fn1<R, ?>, M, ?>> appB) {
102+
return MonadT.super.discardL(appB).coerce();
103+
}
104+
105+
/**
106+
* {@inheritDoc}
107+
*/
108+
@Override
109+
default <B> ReaderT<R, M, A> discardR(Applicative<B, MonadT<Fn1<R, ?>, M, ?>> appB) {
110+
return MonadT.super.discardR(appB).coerce();
111+
}
112+
113+
/**
114+
* {@inheritDoc}
115+
*/
116+
@Override
117+
default <Q, B> ReaderT<Q, M, B> diMap(Fn1<? super Q, ? extends R> lFn, Fn1<? super A, ? extends B> rFn) {
118+
return readerT(q -> runReaderT(lFn.apply(q)).fmap(rFn));
119+
}
120+
121+
/**
122+
* {@inheritDoc}
123+
*/
124+
@Override
125+
default <Q> ReaderT<Q, M, A> diMapL(Fn1<? super Q, ? extends R> fn) {
126+
return (ReaderT<Q, M, A>) Cartesian.super.<Q>diMapL(fn);
127+
}
128+
129+
/**
130+
* {@inheritDoc}
131+
*/
132+
@Override
133+
default <B> ReaderT<R, M, B> diMapR(Fn1<? super A, ? extends B> fn) {
134+
return (ReaderT<R, M, B>) Cartesian.super.<B>diMapR(fn);
135+
}
136+
137+
/**
138+
* {@inheritDoc}
139+
*/
140+
@Override
141+
default <Q> ReaderT<Q, M, A> contraMap(Fn1<? super Q, ? extends R> fn) {
142+
return (ReaderT<Q, M, A>) Cartesian.super.<Q>contraMap(fn);
143+
}
144+
145+
/**
146+
* {@inheritDoc}
147+
*/
148+
@Override
149+
default <C> ReaderT<Tuple2<C, R>, M, Tuple2<C, A>> cartesian() {
150+
return readerT(into((c, r) -> runReaderT(r).fmap(tupler(c))));
151+
}
152+
153+
/**
154+
* {@inheritDoc}
155+
*/
156+
@Override
157+
default ReaderT<R, M, Tuple2<R, A>> carry() {
158+
return (ReaderT<R, M, Tuple2<R, A>>) Cartesian.super.carry();
159+
}
160+
161+
/**
162+
* Lift a {@link Fn1 function} (<code>R -&gt; {@link Monad}&lt;A, M&gt;</code>) into a {@link ReaderT} instance.
163+
*
164+
* @param fn the function
165+
* @param <R> the input type
166+
* @param <M> the returned {@link Monad}
167+
* @param <A> the embedded output type
168+
* @return the {@link ReaderT}
169+
*/
170+
static <R, M extends Monad<?, M>, A> ReaderT<R, M, A> readerT(Fn1<? super R, ? extends Monad<A, M>> fn) {
171+
return fn::apply;
172+
}
173+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.jnape.palatable.lambda.monad.transformer.builtin;
2+
3+
import com.jnape.palatable.lambda.adt.Maybe;
4+
import com.jnape.palatable.lambda.functions.Fn1;
5+
import com.jnape.palatable.lambda.functor.builtin.Identity;
6+
import com.jnape.palatable.lambda.monad.Monad;
7+
import com.jnape.palatable.lambda.monad.transformer.MonadT;
8+
import com.jnape.palatable.traitor.annotations.TestTraits;
9+
import com.jnape.palatable.traitor.runners.Traits;
10+
import org.junit.Test;
11+
import org.junit.runner.RunWith;
12+
import testsupport.EquatableM;
13+
import testsupport.traits.ApplicativeLaws;
14+
import testsupport.traits.FunctorLaws;
15+
import testsupport.traits.MonadLaws;
16+
17+
import static com.jnape.palatable.lambda.adt.Maybe.just;
18+
import static com.jnape.palatable.lambda.monad.transformer.builtin.ReaderT.readerT;
19+
import static org.junit.Assert.assertEquals;
20+
21+
@RunWith(Traits.class)
22+
public class ReaderTTest {
23+
24+
@TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class})
25+
public EquatableM<MonadT<Fn1<Integer, ?>, Identity<?>, ?>, Integer> testSubject() {
26+
return new EquatableM<>(readerT(Identity::new),
27+
readerT -> ((Fn1<Integer, ? extends Monad<?, Identity<?>>>) readerT.run()).apply(1));
28+
}
29+
30+
@Test
31+
public void profunctor() {
32+
assertEquals(new Identity<>(4),
33+
ReaderT.<Integer, Identity<?>, Integer>readerT(Identity::new)
34+
.diMap(String::length, x -> x + 1)
35+
.runReaderT("123"));
36+
}
37+
38+
@Test
39+
public void mapReaderT() {
40+
assertEquals(just(3),
41+
ReaderT.<String, Identity<?>, String>readerT(Identity::new)
42+
.<Identity<String>, Maybe<?>, Integer>mapReaderT(id -> just(id.runIdentity().length()))
43+
.runReaderT("foo"));
44+
}
45+
}

0 commit comments

Comments
 (0)