Skip to content

Commit f076fc8

Browse files
committed
- Adding Applicative#lazyZip, supporting short-circuiting for some types
- FoldRight now operates in terms of Lazy and can be short-circuited
1 parent 4e76e97 commit f076fc8

48 files changed

Lines changed: 1028 additions & 83 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
88
- ***Breaking Change***: `IO` is now sealed and moved to its own package. Most previous constructions using the static
99
factory methods should continue to work (by simply targeting `Supplier` now instead of an anonymous `IO`), but some
1010
might need to be reworked, and subtyping is obviously no longer supported.
11+
- ***Breaking Change***: `FoldRight` now requires `Lazy` as part of its interface to support short-circuiting operations
1112
- `IO` is now stack-safe, regardless of whether the composition nests linearly or recursively
1213

1314
### Added
1415
- `Lazy`, a monad supporting stack-safe lazy evaluation
1516
- `LazyRec`, a function for writing stack-safe recursive algorithms embedded in `Lazy`
17+
- `Applicative#lazyZip`, for zipping two applicatives in a way that might not require evaluation of one applicative
1618

1719
## [3.3.0] - 2019-02-18
1820
### Added

src/main/java/com/jnape/palatable/lambda/adt/Either.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
1010
import com.jnape.palatable.lambda.functor.Applicative;
1111
import com.jnape.palatable.lambda.functor.Bifunctor;
12+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
1213
import com.jnape.palatable.lambda.monad.Monad;
1314
import com.jnape.palatable.lambda.traversable.Traversable;
1415

@@ -20,6 +21,7 @@
2021

2122
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
2223
import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldLeft.foldLeft;
24+
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
2325
import static java.util.Arrays.asList;
2426

2527
/**
@@ -187,54 +189,94 @@ public Either<L, R> peek(Consumer<L> leftConsumer, Consumer<R> rightConsumer) {
187189
@Override
188190
public abstract <V> V match(Function<? super L, ? extends V> leftFn, Function<? super R, ? extends V> rightFn);
189191

192+
/**
193+
* {@inheritDoc}
194+
*/
190195
@Override
191196
public <C> Choice3<L, R, C> diverge() {
192197
return match(Choice3::a, Choice3::b);
193198
}
194199

200+
/**
201+
* {@inheritDoc}
202+
*/
195203
@Override
196204
public final <R2> Either<L, R2> fmap(Function<? super R, ? extends R2> fn) {
197205
return Monad.super.<R2>fmap(fn).coerce();
198206
}
199207

208+
/**
209+
* {@inheritDoc}
210+
*/
200211
@Override
201212
@SuppressWarnings("unchecked")
202213
public final <L2> Either<L2, R> biMapL(Function<? super L, ? extends L2> fn) {
203214
return (Either<L2, R>) Bifunctor.super.biMapL(fn);
204215
}
205216

217+
/**
218+
* {@inheritDoc}
219+
*/
206220
@Override
207221
@SuppressWarnings("unchecked")
208222
public final <R2> Either<L, R2> biMapR(Function<? super R, ? extends R2> fn) {
209223
return (Either<L, R2>) Bifunctor.super.biMapR(fn);
210224
}
211225

226+
/**
227+
* {@inheritDoc}
228+
*/
212229
@Override
213230
public final <L2, R2> Either<L2, R2> biMap(Function<? super L, ? extends L2> leftFn,
214231
Function<? super R, ? extends R2> rightFn) {
215232
return match(l -> left(leftFn.apply(l)), r -> right(rightFn.apply(r)));
216233
}
217234

235+
/**
236+
* {@inheritDoc}
237+
*/
218238
@Override
219239
public final <R2> Either<L, R2> pure(R2 r2) {
220240
return right(r2);
221241
}
222242

243+
/**
244+
* {@inheritDoc}
245+
*/
223246
@Override
224247
public final <R2> Either<L, R2> zip(Applicative<Function<? super R, ? extends R2>, Either<L, ?>> appFn) {
225248
return appFn.<Either<L, Function<? super R, ? extends R2>>>coerce().flatMap(this::biMapR);
226249
}
227250

251+
/**
252+
* {@inheritDoc}
253+
*/
254+
@Override
255+
public <R2> Lazy<Either<L, R2>> lazyZip(
256+
Lazy<Applicative<Function<? super R, ? extends R2>, Either<L, ?>>> lazyAppFn) {
257+
return match(l -> lazy(left(l)),
258+
r -> lazyAppFn.fmap(eitherLF -> eitherLF.<R2>fmap(f -> f.apply(r)).coerce()));
259+
}
260+
261+
/**
262+
* {@inheritDoc}
263+
*/
228264
@Override
229265
public final <R2> Either<L, R2> discardL(Applicative<R2, Either<L, ?>> appB) {
230266
return Monad.super.discardL(appB).coerce();
231267
}
232268

269+
/**
270+
* {@inheritDoc}
271+
*/
233272
@Override
234273
public final <R2> Either<L, R> discardR(Applicative<R2, Either<L, ?>> appB) {
235274
return Monad.super.discardR(appB).coerce();
236275
}
237276

277+
/**
278+
* {@inheritDoc}
279+
*/
238280
@Override
239281
@SuppressWarnings("unchecked")
240282
public final <R2, App extends Applicative, TravB extends Traversable<R2, Either<L, ?>>, AppB extends Applicative<R2, App>, AppTrav extends Applicative<TravB, App>> AppTrav traverse(

src/main/java/com/jnape/palatable/lambda/adt/Maybe.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
1010
import com.jnape.palatable.lambda.functor.Applicative;
1111
import com.jnape.palatable.lambda.functor.Functor;
12+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
1213
import com.jnape.palatable.lambda.monad.Monad;
1314
import com.jnape.palatable.lambda.traversable.Traversable;
1415

@@ -22,6 +23,7 @@
2223
import static com.jnape.palatable.lambda.adt.Unit.UNIT;
2324
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
2425
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
26+
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
2527

2628
/**
2729
* The optional type, representing a potentially absent value. This is lambda's analog of {@link Optional}, supporting
@@ -125,36 +127,70 @@ public final <B> Maybe<B> fmap(Function<? super A, ? extends B> fn) {
125127
return Monad.super.<B>fmap(fn).coerce();
126128
}
127129

130+
/**
131+
* {@inheritDoc}
132+
*/
128133
@Override
129134
public final <B> Maybe<B> zip(Applicative<Function<? super A, ? extends B>, Maybe> appFn) {
130135
return Monad.super.zip(appFn).coerce();
131136
}
132137

138+
/**
139+
* Terminate early if this is a {@link Nothing}; otherwise, continue the {@link Applicative#zip zip}.
140+
*
141+
* @param lazyAppFn the lazy other applicative instance
142+
* @param <B> the result type
143+
* @return the zipped {@link Maybe}
144+
*/
145+
@Override
146+
public <B> Lazy<Maybe<B>> lazyZip(Lazy<Applicative<Function<? super A, ? extends B>, Maybe>> lazyAppFn) {
147+
return match(constantly(lazy(nothing())),
148+
a -> lazyAppFn.fmap(maybeF -> maybeF.<B>fmap(f -> f.apply(a)).coerce()));
149+
}
150+
151+
/**
152+
* {@inheritDoc}
153+
*/
133154
@Override
134155
public final <B> Maybe<B> discardL(Applicative<B, Maybe> appB) {
135156
return Monad.super.discardL(appB).coerce();
136157
}
137158

159+
/**
160+
* {@inheritDoc}
161+
*/
138162
@Override
139163
public final <B> Maybe<A> discardR(Applicative<B, Maybe> appB) {
140164
return Monad.super.discardR(appB).coerce();
141165
}
142166

167+
/**
168+
* {@inheritDoc}
169+
*/
143170
@Override
144171
public final <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
145172
return match(constantly(nothing()), f.andThen(Applicative::coerce));
146173
}
147174

175+
/**
176+
* {@inheritDoc}
177+
*/
148178
@Override
149179
public <B> Choice3<Unit, A, B> diverge() {
150180
return match(Choice3::a, Choice3::b);
151181
}
152182

183+
/**
184+
* {@inheritDoc}
185+
*/
153186
@Override
154187
public Tuple2<Maybe<Unit>, Maybe<A>> project() {
155188
return CoProduct2.super.project().into(HList::tuple);
156189
}
157190

191+
/**
192+
* {@inheritDoc}
193+
*/
158194
@Override
159195
public Choice2<A, Unit> invert() {
160196
return match(Choice2::b, Choice2::a);

src/main/java/com/jnape/palatable/lambda/adt/These.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
66
import com.jnape.palatable.lambda.functor.Applicative;
77
import com.jnape.palatable.lambda.functor.Bifunctor;
8+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
89
import com.jnape.palatable.lambda.monad.Monad;
910
import com.jnape.palatable.lambda.traversable.Traversable;
1011

@@ -14,6 +15,7 @@
1415
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
1516
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
1617
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
18+
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
1719

1820
/**
1921
* The coproduct of a coproduct (<code>{@link CoProduct2}&lt;A, B&gt;</code>) and its product (<code>{@link
@@ -96,6 +98,12 @@ public final <C> These<A, C> zip(Applicative<Function<? super B, ? extends C>, T
9698
return Monad.super.zip(appFn).coerce();
9799
}
98100

101+
@Override
102+
public <C> Lazy<These<A, C>> lazyZip(Lazy<Applicative<Function<? super B, ? extends C>, These<A, ?>>> lazyAppFn) {
103+
return projectA().<Lazy<These<A, C>>>fmap(a -> lazy(a(a)))
104+
.orElseGet(() -> Monad.super.lazyZip(lazyAppFn).fmap(Applicative::coerce));
105+
}
106+
99107
/**
100108
* {@inheritDoc}
101109
*/

src/main/java/com/jnape/palatable/lambda/adt/Try.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
77
import com.jnape.palatable.lambda.functor.Applicative;
88
import com.jnape.palatable.lambda.functor.BoundedBifunctor;
9+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
910
import com.jnape.palatable.lambda.monad.Monad;
1011
import com.jnape.palatable.lambda.traversable.Traversable;
1112

@@ -18,6 +19,7 @@
1819
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
1920
import static com.jnape.palatable.lambda.functions.builtin.fn1.Upcast.upcast;
2021
import static com.jnape.palatable.lambda.functions.builtin.fn2.Peek2.peek2;
22+
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
2123

2224
/**
2325
* A {@link Monad} of the evaluation outcome of an expression that might throw. Try/catch/finally semantics map to
@@ -139,36 +141,63 @@ public final <L> Either<L, A> toEither(Function<? super T, ? extends L> fn) {
139141
return match(fn.andThen(Either::left), Either::right);
140142
}
141143

144+
/**
145+
* {@inheritDoc}
146+
*/
142147
@Override
143148
public <B> Try<T, B> fmap(Function<? super A, ? extends B> fn) {
144149
return Monad.super.<B>fmap(fn).coerce();
145150
}
146151

152+
/**
153+
* {@inheritDoc}
154+
*/
147155
@Override
148156
public <B> Try<T, B> flatMap(Function<? super A, ? extends Monad<B, Try<T, ?>>> f) {
149157
return match(Try::failure, a -> f.apply(a).coerce());
150158
}
151159

160+
/**
161+
* {@inheritDoc}
162+
*/
152163
@Override
153164
public <B> Try<T, B> pure(B b) {
154165
return success(b);
155166
}
156167

168+
/**
169+
* {@inheritDoc}
170+
*/
157171
@Override
158172
public <B> Try<T, B> zip(Applicative<Function<? super A, ? extends B>, Try<T, ?>> appFn) {
159173
return Monad.super.zip(appFn).coerce();
160174
}
161175

176+
@Override
177+
public <B> Lazy<Try<T, B>> lazyZip(Lazy<Applicative<Function<? super A, ? extends B>, Try<T, ?>>> lazyAppFn) {
178+
return match(f -> lazy(failure(f)),
179+
s -> lazyAppFn.fmap(tryF -> tryF.<B>fmap(f -> f.apply(s)).coerce()));
180+
}
181+
182+
/**
183+
* {@inheritDoc}
184+
*/
162185
@Override
163186
public <B> Try<T, B> discardL(Applicative<B, Try<T, ?>> appB) {
164187
return Monad.super.discardL(appB).coerce();
165188
}
166189

190+
/**
191+
* {@inheritDoc}
192+
*/
167193
@Override
168194
public <B> Try<T, A> discardR(Applicative<B, Try<T, ?>> appB) {
169195
return Monad.super.discardR(appB).coerce();
170196
}
171197

198+
/**
199+
* {@inheritDoc}
200+
*/
172201
@Override
173202
@SuppressWarnings("unchecked")
174203
public <B, App extends Applicative, TravB extends Traversable<B, Try<T, ?>>, AppB extends Applicative<B, App>, AppTrav extends Applicative<TravB, App>> AppTrav traverse(
@@ -177,17 +206,26 @@ public <B> Try<T, A> discardR(Applicative<B, Try<T, ?>> appB) {
177206
a -> fn.apply(a).fmap(Try::success).<TravB>fmap(Applicative::coerce).coerce());
178207
}
179208

209+
/**
210+
* {@inheritDoc}
211+
*/
180212
@Override
181213
public <U extends Throwable, D> Try<U, D> biMap(Function<? super T, ? extends U> lFn,
182214
Function<? super A, ? extends D> rFn) {
183215
return match(t -> failure(lFn.apply(t)), a -> success(rFn.apply(a)));
184216
}
185217

218+
/**
219+
* {@inheritDoc}
220+
*/
186221
@Override
187222
public <U extends Throwable> Try<U, A> biMapL(Function<? super T, ? extends U> fn) {
188223
return (Try<U, A>) BoundedBifunctor.super.<U>biMapL(fn);
189224
}
190225

226+
/**
227+
* {@inheritDoc}
228+
*/
191229
@Override
192230
public <B> Try<T, B> biMapR(Function<? super A, ? extends B> fn) {
193231
return (Try<T, B>) BoundedBifunctor.super.<B>biMapR(fn);

0 commit comments

Comments
 (0)