Skip to content

Commit 067056f

Browse files
committed
MonadT changes
- MonadBase lifting infrastructure separate from MonadT - MonadT no longer demands run method but merely conjoins MonadBase to Monad - added StateT and WriterT - added MonadReader and MonadWriter - added Lift type - EqualityM replaced with Equivalence
1 parent 803c2c9 commit 067056f

Some content is hidden

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

47 files changed

+1661
-434
lines changed

CHANGELOG.md

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
55

66
## [Unreleased]
77
### Changed
8-
- ***Breaking Change***: `MonadT` is now witnessed by a parameter for better subtyping
8+
- ***Breaking Change***: `MonadT` is now witnessed by a parameter for better subtyping, and no longer requires a common
9+
`run` interface
910
- ***Breaking Change***: `Applicative#zip` and derivatives evaluate from left to right now across the board.
11+
- ***Breaking Change***: `testsupport.EquatableM` replaced with `Equivalence`
1012
- `Alter` now merely requires an `Fn1` instead of an explicit `Effect`
11-
- `IO` now internally trampolines all forms of composition, including lazyZip;
12-
sequencing very large iterables of IO will work, if you have the heap, and
13+
- `IO` now internally trampolines all forms of composition, including `lazyZip`;
14+
sequencing very large iterables of `IO` will work, if you have the heap, and
1315
retain parallelization inflection points
1416

1517
### Added
18+
- `MonadError`, monads that can be thrown to and caught from, with defaults for `IO`, `Either`, `Maybe`, and `Try`
1619
- `Optic#andThen`, `Optic#compose`, and other defaults added
1720
- `Prism#andThen`, `Prism#compose` begets another `Prism`
1821
- `Prism#fromPartial` public interfaces
@@ -22,11 +25,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
2225
- `IO#monitorSync`, for wrapping an `IO` in a `synchronized` block on a given lock object
2326
- `IO#pin`, for pinning an `IO` to an `Executor` without yet executing it
2427
- `IO#fuse`, for fusing the fork opportunities of a given `IO` into a single linearized `IO`
25-
- `IO#exceptionallyIO`, like `exceptionally` but recover inside another `IO`
2628
- `IO#memoize`, for memoizing an `IO` by caching its first successful result
27-
- `MonadError`, monads that can be thrown to and caught from
2829
- `Tuple2-8#fromIterable`, for populating a `TupleN` with the first `N` elements of an `Iterable`
2930
- `Fn2#curry`, for converting an `Fn1<Tuple2<A,B>,C>` to an `Fn2<A,B,C>`
31+
- `MonadBase`, an interface representing lifting infrastructure for `Monad`s
32+
- `Lift`, an existentially-quantified lifting function for some `MonadBase` type
33+
- `MonadReader` and `MonadWriter`, general interfaces for reading from an environment and accumulating results
34+
- `StateT`, the `State` monad transformer
35+
- `WriterT`, a monad transformer for an accumulation and a value
36+
- `EquivalenceTrait`, a traitor `Trait` to make it easier to test properties of type-classes with a separate equivalence
37+
relation
3038

3139
## [4.0.0] - 2019-05-20
3240
### Changed
@@ -101,11 +109,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
101109

102110
## [3.2.0] - 2018-12-08
103111
### Changed
104-
- ***Breaking Change***: `Difference` and `Intersection` no longer instances of `Semigroup` and moved to `functions.builtin.fn2` package
112+
- ***Breaking Change***: `Difference` and `Intersection` no longer instances of `Semigroup` and moved to
113+
`functions.builtin.fn2` package
105114
- ***Breaking Change***: `Absent` moved to `semigroup.builtin` package
106115
- ***Breaking Change***: `Effect#accept()` is now the required method to implement in the functional interface
107116
- ***Breaking Change***: `Fn0#apply()` is now the required method to implement in the functional interface
108-
- ***Breaking Change***: `GTBy`, `GT`, `LTBy`, `LT`, `GTEBy`, `GTE`, `LTEBy`, and `LTE` take the right-hand side first for more intuitive partial application
117+
- ***Breaking Change***: `GTBy`, `GT`, `LTBy`, `LT`, `GTEBy`, `GTE`, `LTEBy`, and `LTE` take the right-hand side first
118+
for more intuitive partial application
109119
- ***Breaking Change***: `Effect` now returns an `IO`
110120
- `RightAny` overload returns `Monoid`
111121
- monoids now all fold with respect to `foldMap`
@@ -207,14 +217,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
207217
### Changed
208218
- ***Breaking Change***: `Sequence` now has two more type parameters to aid in inference
209219
- ***Breaking Change***: `Traversable#traverse` now has three more type parameters to aid in inference
210-
- ***Breaking Change***: `Monad#zip` now forces `m a -> b` before `m a` in default `Applicative#zip` implementation; this is only breaking for types that are sensitive to computation order (the resulting values are the same)
211-
- ***Breaking Change***: `TypeSafeKey` is now dually parametric (single parameter analog is preserved in `TypeSafeKey.Simple`)
220+
- ***Breaking Change***: `Monad#zip` now forces `m a -> b` before `m a` in default `Applicative#zip` implementation;
221+
this is only breaking for types that are sensitive to computation order (the resulting values are the same)
222+
- ***Breaking Change***: `TypeSafeKey` is now dually parametric (single parameter analog is preserved in
223+
`TypeSafeKey.Simple`)
212224
- `Bifunctor` is now a `BoundedBifunctor` where both parameter upper bounds are `Object`
213225
- `Peek2` now accepts the more general `BoundedBifunctor`
214226
- `Identity`, `Compose`, and `Const` functors all have better `toString` implementations
215227
- `Into3-8` now supports functions with parameter variance
216228
- `HListLens#tail` is now covariant in `Tail` parameter
217-
- More functions now automatically deforest nested calls (`concat` `cons`, `cycle`, `distinct`, `drop`, `dropwhile`, `filter`, `map`, `reverse`, `snoc`, `take`, `takewhile`, `tail`)
229+
- More functions now automatically deforest nested calls (`concat` `cons`, `cycle`, `distinct`, `drop`, `dropwhile`,
230+
`filter`, `map`, `reverse`, `snoc`, `take`, `takewhile`, `tail`)
218231
- `Flatten` calls `Iterator#hasNext` less aggressively, allowing for better laziness
219232
- `Lens` subtypes `LensLike`
220233
- `View`/`Set`/`Over` now only require `LensLike`
@@ -280,7 +293,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
280293
- `CollectionLens#asSet(Function)`, a proper analog of `CollectionLens#asSet()` that uses defensive copies
281294
- `CollectionLens#asStream(Function)`, a proper analog of `CollectionLens#asStream()` that uses defensive copies
282295
- Explicitly calling attention to all unlawful lenses in their documentation
283-
- `Peek` and `Peek2`, for "peeking" at the value contained inside any given `Functor` or `Bifunctor` with given side-effects
296+
- `Peek` and `Peek2`, for "peeking" at the value contained inside any given `Functor` or `Bifunctor` with given
297+
side-effects
284298
- `Trampoline` and `RecursiveResult` for modeling primitive tail-recursive functions that can be trampolined
285299

286300
### Removed
@@ -390,7 +404,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
390404
- `Const` supports value equality
391405
- `partition` now only requires iterables of `CoProudct2`
392406
- `CoProductN`s receive a unification parameter, which trickles down to `Either` and `Choice`s
393-
- `Concat` now represents a monoid for `Iterable`; previous `Concat` semigroup and monoid renamed to more appropriate `AddAll`
407+
- `Concat` now represents a monoid for `Iterable`; previous `Concat` semigroup and monoid renamed to more appropriate
408+
`AddAll`
394409
- `Lens` is now an instance of `Profunctor`
395410

396411
### Added
@@ -400,14 +415,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
400415
- `empty`, used to test if an Iterable is empty
401416
- `groupBy`, for folding an Iterable into a Map given a key function
402417
- `Applicative` arrives; all functors gain applicative properties
403-
- `Traversable` arrives; `SingletonHList`, `Tuple*`, `Choice*`, `Either`, `Identity`, and `Const` gain traversable properties
418+
- `Traversable` arrives; `SingletonHList`, `Tuple*`, `Choice*`, `Either`, `Identity`, and `Const` gain traversable
419+
properties
404420
- `TraversableOptional` and `TraversableIterable` for adapting `Optional` and `Iterable`, respectively, to `Traversable`
405421
- `sequence` for wrapping a traversable in an applicative during traversal
406422
- `Compose`, an applicative functor that represents type-level functor composition
407423

408424
## [1.5.6] - 2017-02-11
409425
### Changed
410-
- `CoProductN.[a-e]()` static factory methods moved to equivalent `ChoiceN` class. Coproduct interfaces now solely represent methods, no longer have anonymous implementations, and no longer require a `Functor` constraint
426+
- `CoProductN.[a-e]()` static factory methods moved to equivalent `ChoiceN` class. Coproduct interfaces now solely
427+
represent methods, no longer have anonymous implementations, and no longer require a `Functor` constraint
411428

412429
### Added
413430
- `ChoiceN` types, representing concrete coproduct implementations that are also `Functor` and `BiFunctor`
@@ -461,7 +478,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
461478

462479
## [1.4] - 2016-08-08
463480
### Changed
464-
- All function input values become `java.util.function` types, and all function output values remain lambda types, for better compatibility
481+
- All function input values become `java.util.function` types, and all function output values remain lambda types, for
482+
better compatibility
465483

466484
## [1.3] - 2016-07-31
467485
### Changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
</developers>
5555

5656
<properties>
57-
<traitor.version>1.2</traitor.version>
57+
<traitor.version>1.3.0</traitor.version>
5858
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
5959
<hamcrest.version>2.1</hamcrest.version>
6060
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>

src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
import com.jnape.palatable.lambda.functor.Bifunctor;
1111
import com.jnape.palatable.lambda.functor.builtin.Lazy;
1212
import com.jnape.palatable.lambda.monad.Monad;
13+
import com.jnape.palatable.lambda.monad.MonadWriter;
1314
import com.jnape.palatable.lambda.traversable.Traversable;
1415

1516
import java.util.Map;
1617

1718
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
19+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
1820
import static com.jnape.palatable.lambda.functions.builtin.fn1.Uncons.uncons;
21+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Both.both;
1922

2023
/**
2124
* A 2-element tuple product type, implemented as a specialized HList. Supports random access.
@@ -32,7 +35,7 @@
3235
public class Tuple2<_1, _2> extends HCons<_1, SingletonHList<_2>> implements
3336
Product2<_1, _2>,
3437
Map.Entry<_1, _2>,
35-
Monad<_2, Tuple2<_1, ?>>,
38+
MonadWriter<_1, _2, Tuple2<_1, ?>>,
3639
Bifunctor<_1, _2, Tuple2<?, ?>>,
3740
Traversable<_2, Tuple2<_1, ?>> {
3841

@@ -45,6 +48,22 @@ public class Tuple2<_1, _2> extends HCons<_1, SingletonHList<_2>> implements
4548
_2 = tail.head();
4649
}
4750

51+
/**
52+
* {@inheritDoc}
53+
*/
54+
@Override
55+
public <_3> Tuple2<_1, Tuple2<_2, _3>> listens(Fn1<? super _1, ? extends _3> fn) {
56+
return fmap(both(id(), constantly(fn.apply(_1))));
57+
}
58+
59+
/**
60+
* {@inheritDoc}
61+
*/
62+
@Override
63+
public Tuple2<_1, _2> censor(Fn1<? super _1, ? extends _1> fn) {
64+
return biMapL(fn);
65+
}
66+
4867
/**
4968
* {@inheritDoc}
5069
*/
@@ -106,7 +125,7 @@ public Tuple2<_2, _1> invert() {
106125
*/
107126
@Override
108127
public <_2Prime> Tuple2<_1, _2Prime> fmap(Fn1<? super _2, ? extends _2Prime> fn) {
109-
return Monad.super.<_2Prime>fmap(fn).coerce();
128+
return MonadWriter.super.<_2Prime>fmap(fn).coerce();
110129
}
111130

112131
/**
@@ -150,7 +169,7 @@ public <_2Prime> Tuple2<_1, _2Prime> pure(_2Prime _2Prime) {
150169
@Override
151170
public <_2Prime> Tuple2<_1, _2Prime> zip(
152171
Applicative<Fn1<? super _2, ? extends _2Prime>, Tuple2<_1, ?>> appFn) {
153-
return Monad.super.zip(appFn).coerce();
172+
return MonadWriter.super.zip(appFn).coerce();
154173
}
155174

156175
/**
@@ -159,23 +178,23 @@ public <_2Prime> Tuple2<_1, _2Prime> zip(
159178
@Override
160179
public <_2Prime> Lazy<Tuple2<_1, _2Prime>> lazyZip(
161180
Lazy<? extends Applicative<Fn1<? super _2, ? extends _2Prime>, Tuple2<_1, ?>>> lazyAppFn) {
162-
return Monad.super.lazyZip(lazyAppFn).fmap(Monad<_2Prime, Tuple2<_1, ?>>::coerce);
181+
return MonadWriter.super.lazyZip(lazyAppFn).fmap(Monad<_2Prime, Tuple2<_1, ?>>::coerce);
163182
}
164183

165184
/**
166185
* {@inheritDoc}
167186
*/
168187
@Override
169188
public <_2Prime> Tuple2<_1, _2Prime> discardL(Applicative<_2Prime, Tuple2<_1, ?>> appB) {
170-
return Monad.super.discardL(appB).coerce();
189+
return MonadWriter.super.discardL(appB).coerce();
171190
}
172191

173192
/**
174193
* {@inheritDoc}
175194
*/
176195
@Override
177196
public <_2Prime> Tuple2<_1, _2> discardR(Applicative<_2Prime, Tuple2<_1, ?>> appB) {
178-
return Monad.super.discardR(appB).coerce();
197+
return MonadWriter.super.discardR(appB).coerce();
179198
}
180199

181200
/**

src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ public <A, B> Maybe<B> get(TypeSafeKey<A, B> key) {
5959
* @throws NoSuchElementException if the key is unmapped
6060
*/
6161
public <V> V demand(TypeSafeKey<?, V> key) throws NoSuchElementException {
62-
return get(key).orElseThrow(() -> new NoSuchElementException("Demanded value for key " + key + ", but couldn't find one."));
62+
return get(key).orElseThrow(() -> new NoSuchElementException("Demanded value for key " + key
63+
+ ", but couldn't find one."));
6364
}
6465

6566
/**

src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public boolean equals(Object obj) {
6161
* @return the new {@link TypeSafeKey}
6262
*/
6363
@Override
64-
default <C> TypeSafeKey<A, C>andThen(Iso.Simple<B, C> f) {
64+
default <C> TypeSafeKey<A, C> andThen(Iso.Simple<B, C> f) {
6565
Iso.Simple<A, C> composed = Iso.Simple.super.andThen(f);
6666
return new TypeSafeKey<A, C>() {
6767
@Override

src/main/java/com/jnape/palatable/lambda/functions/Fn1.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import com.jnape.palatable.lambda.functor.builtin.Lazy;
1212
import com.jnape.palatable.lambda.internal.Runtime;
1313
import com.jnape.palatable.lambda.monad.Monad;
14+
import com.jnape.palatable.lambda.monad.MonadReader;
15+
import com.jnape.palatable.lambda.monad.MonadWriter;
1416

1517
import java.util.function.Function;
1618

@@ -26,7 +28,8 @@
2628
*/
2729
@FunctionalInterface
2830
public interface Fn1<A, B> extends
29-
Monad<B, Fn1<A, ?>>,
31+
MonadReader<A, B, Fn1<A, ?>>,
32+
MonadWriter<A, B, Fn1<A, ?>>,
3033
Cartesian<A, B, Fn1<?, ?>>,
3134
Cocartesian<A, B, Fn1<?, ?>> {
3235

@@ -83,6 +86,30 @@ default Function<A, B> toFunction() {
8386
return this::apply;
8487
}
8588

89+
/**
90+
* {@inheritDoc}
91+
*/
92+
@Override
93+
default Fn1<A, B> local(Fn1<? super A, ? extends A> fn) {
94+
return contraMap(fn);
95+
}
96+
97+
/**
98+
* {@inheritDoc}
99+
*/
100+
@Override
101+
default <C> Fn1<A, Tuple2<B, C>> listens(Fn1<? super A, ? extends C> fn) {
102+
return carry().fmap(t -> t.<C>biMapL(fn).invert());
103+
}
104+
105+
/**
106+
* {@inheritDoc}
107+
*/
108+
@Override
109+
default Fn1<A, B> censor(Fn1<? super A, ? extends A> fn) {
110+
return a -> apply(fn.apply(a));
111+
}
112+
86113
/**
87114
* {@inheritDoc}
88115
*/
@@ -116,7 +143,7 @@ default <C> Fn1<A, C> pure(C c) {
116143
*/
117144
@Override
118145
default <C> Fn1<A, C> zip(Applicative<Fn1<? super B, ? extends C>, Fn1<A, ?>> appFn) {
119-
return Monad.super.zip(appFn).coerce();
146+
return MonadReader.super.zip(appFn).coerce();
120147
}
121148

122149
/**
@@ -132,23 +159,23 @@ default <C> Fn1<A, C> zip(Fn2<A, B, C> appFn) {
132159
*/
133160
@Override
134161
default <C> Lazy<Fn1<A, C>> lazyZip(Lazy<? extends Applicative<Fn1<? super B, ? extends C>, Fn1<A, ?>>> lazyAppFn) {
135-
return Monad.super.lazyZip(lazyAppFn).fmap(Monad<C, Fn1<A, ?>>::coerce);
162+
return MonadReader.super.lazyZip(lazyAppFn).fmap(Monad<C, Fn1<A, ?>>::coerce);
136163
}
137164

138165
/**
139166
* {@inheritDoc}
140167
*/
141168
@Override
142169
default <C> Fn1<A, C> discardL(Applicative<C, Fn1<A, ?>> appB) {
143-
return Monad.super.discardL(appB).coerce();
170+
return MonadReader.super.discardL(appB).coerce();
144171
}
145172

146173
/**
147174
* {@inheritDoc}
148175
*/
149176
@Override
150177
default <C> Fn1<A, B> discardR(Applicative<C, Fn1<A, ?>> appB) {
151-
return Monad.super.discardR(appB).coerce();
178+
return MonadReader.super.discardR(appB).coerce();
152179
}
153180

154181
/**
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.jnape.palatable.lambda.functions.specialized;
2+
3+
import com.jnape.palatable.lambda.internal.Runtime;
4+
import com.jnape.palatable.lambda.monad.Monad;
5+
import com.jnape.palatable.lambda.monad.MonadBase;
6+
7+
/**
8+
* Generalized, portable lifting operation for lifting a {@link Monad} into a {@link MonadBase}.
9+
*
10+
* @param <B> the {@link MonadBase} to lift into
11+
*/
12+
@FunctionalInterface
13+
public interface Lift<B extends MonadBase<?, ?, B>> {
14+
15+
<A, M extends Monad<?, M>> MonadBase<M, A, B> checkedApply(Monad<A, M> ga)
16+
throws Throwable;
17+
18+
default <A, M extends Monad<?, M>, MBA extends MonadBase<M, A, B>> MBA apply(Monad<A, M> ma) {
19+
try {
20+
@SuppressWarnings("unchecked") MBA MBA = (MBA) checkedApply(ma);
21+
return MBA;
22+
} catch (Throwable t) {
23+
throw Runtime.throwChecked(t);
24+
}
25+
}
26+
27+
/**
28+
* Static method to aid inference.
29+
*
30+
* @param lift the {@link Lift}
31+
* @param <B> the {@link MonadBase} to lift into
32+
* @return the {@link Lift}
33+
*/
34+
static <B extends MonadBase<?, ?, B>> Lift<B> lift(Lift<B> lift) {
35+
return lift;
36+
}
37+
}

src/main/java/com/jnape/palatable/lambda/functor/Applicative.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,6 @@ default <B> Lazy<? extends Applicative<B, App>> lazyZip(
6464
return lazyAppFn.fmap(this::zip);
6565
}
6666

67-
@Override
68-
default <B> Applicative<B, App> fmap(Fn1<? super A, ? extends B> fn) {
69-
return zip(pure(fn));
70-
}
71-
7267
/**
7368
* Sequence both this <code>Applicative</code> and <code>appB</code>, discarding this <code>Applicative's</code>
7469
* result and returning <code>appB</code>. This is generally useful for sequentially performing side-effects.
@@ -92,4 +87,12 @@ default <B> Applicative<B, App> discardL(Applicative<B, App> appB) {
9287
default <B> Applicative<A, App> discardR(Applicative<B, App> appB) {
9388
return zip(appB.fmap(constantly(id())));
9489
}
90+
91+
/**
92+
* {@inheritDoc}
93+
*/
94+
@Override
95+
default <B> Applicative<B, App> fmap(Fn1<? super A, ? extends B> fn) {
96+
return zip(pure(fn));
97+
}
9598
}

0 commit comments

Comments
 (0)