Skip to content

Commit

Permalink
Modularization: decoupled Lazy from Option and Seq (#2280)
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldietrich committed Aug 3, 2018
1 parent 7ec6452 commit e01b510
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 67 deletions.
56 changes: 20 additions & 36 deletions vavr/src/main/java/io/vavr/Lazy.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
package io.vavr;

import io.vavr.collection.Iterator;
import io.vavr.collection.Seq;
import io.vavr.collection.Vector;
import io.vavr.control.Option;

import java.io.IOException;
import java.io.ObjectOutputStream;
Expand Down Expand Up @@ -55,8 +52,6 @@
*
* @author Daniel Dietrich
*/
// DEV-NOTE: No flatMap and orElse because this more like a Functor than a Monad.
// It represents a value rather than capturing a specific state.
public final class Lazy<T> implements Value<T>, Supplier<T>, Serializable {

private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -102,21 +97,6 @@ public static <T> Lazy<T> of(Supplier<? extends T> supplier) {
}
}

/**
* Reduces many {@code Lazy} values into a single {@code Lazy} by transforming an
* {@code Iterable<Lazy<? extends T>>} into a {@code Lazy<Seq<T>>}.
*
* @param <T> Type of the lazy values.
* @param values An iterable of lazy values.
* @return A lazy sequence of values.
* @throws NullPointerException if values is null
*/
@SuppressWarnings("Convert2MethodRef") // TODO should be fixed in JDK 9 and Idea
public static <T> Lazy<Seq<T>> sequence(Iterable<? extends Lazy<? extends T>> values) {
Objects.requireNonNull(values, "values is null");
return Lazy.of(() -> Vector.ofAll(values).map(lazy -> lazy.get()));
}

/**
* Creates a real _lazy value_ of type {@code T}, backed by a {@linkplain java.lang.reflect.Proxy} which delegates
* to a {@code Lazy} instance.
Expand Down Expand Up @@ -146,28 +126,32 @@ public static <T> T val(Supplier<? extends T> supplier, Class<T> type) {
* Examples:
*
* <pre>{@code
* // = Lazy(?)
* Lazy<Option<Integer>> val1 = Lazy.of(() -> 1).filter(i -> false);
*
* // = None
* val1.get();
* // after that, val1 = Lazy(None)
* Lazy<String> hank = Lazy.of(() -> "Hank").filter(name -> name.startsWith("H"), name -> "Nikola");
*
* // = Lazy(?)
* Lazy<Option<Integer>> val2 = Lazy.of(() -> 1).filter(i -> true);
*
* // = Some(1)
* val2.get();
* // after that, val2 = Lazy(Some(1))
* Lazy<String> nikola = Lazy.of(() -> "Hank").filter(name -> name.startsWith("N"), name -> "Nikola");
* }</pre>
*
* @param predicate a predicate that states whether the element passes the filter (true) or not (false)
* @return a new, unevaluated {@code Lazy<Option<T>>} instance
* @throws NullPointerException if {@code predicate} is null
* @param defaultValue creates a default value, if this lazy value does not make it through the filter
* @return a new, unevaluated {@code Lazy<T>} instance
* @throws NullPointerException if {@code predicate} or {@code defaultValue} is null
*/
public Lazy<Option<T>> filter(Predicate<? super T> predicate) {
public Lazy<T> filter(Predicate<? super T> predicate, Function<? super T, ? extends T> defaultValue) {
Objects.requireNonNull(predicate, "predicate is null");
return map(t -> Option.some(t).filter(predicate));
Objects.requireNonNull(predicate, "defaultValue is null");
return map(t -> predicate.test(t) ? t : defaultValue.apply(t));
}

/**
* Returns a new {@code Lazy} instance that applies the given mapper when being evaluated,
* e.g. by calling {@link #get()}.
*
* @param mapper a function that maps this underlying lazy value to a new {@code Lazy} instance.
* @param <U> mapped value type
* @return a new {@code Lazy} instance
*/
public <U> Lazy<U> flatMap(Function<? super T, Lazy<? extends U>> mapper) {
return Lazy.of(() -> mapper.apply(get()).get());
}

/**
Expand Down
44 changes: 13 additions & 31 deletions vavr/src/test/java/io/vavr/LazyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,52 +119,34 @@ public void shouldPeek() {
assertThat(peek).isSameAs(lazy);
}

// -- sequence(Iterable)
// -- flatMap

@Test
public void shouldSequenceEmpty() {
final List<Lazy<Integer>> testee = List.empty();
final Lazy<Seq<Integer>> sequence = Lazy.sequence(testee);
assertThat(sequence.get()).isEqualTo(Vector.empty());
}

@Test
public void shouldSequenceNonEmptyLazy() {
final List<Lazy<Integer>> testee = List.of(1, 2, 3).map(i -> Lazy.of(() -> i));
final Lazy<Seq<Integer>> sequence = Lazy.sequence(testee);
assertThat(sequence.get()).isEqualTo(Vector.of(1, 2, 3));
}

@Test
public void shouldNotEvaluateEmptySequence() {
final List<Lazy<Integer>> testee = List.empty();
final Lazy<Seq<Integer>> sequence = Lazy.sequence(testee);
assertThat(sequence.isEvaluated()).isFalse();
public void shouldFlatMapLazyValue() {
final Lazy<Integer> testee = Lazy.of(() -> 42);
final Lazy<Integer> expected = Lazy.of(() -> 21);
assertThat(testee.flatMap(i -> Lazy.of(() -> i / 2))).isEqualTo(expected);
}

@Test
public void shouldNotEvaluateNonEmptySequence() {
final List<Lazy<Integer>> testee = List.of(1, 2, 3).map(i -> Lazy.of(() -> i));
final Lazy<Seq<Integer>> sequence = Lazy.sequence(testee);
assertThat(sequence.isEvaluated()).isFalse();
}
// -- map

@Test
public void shouldMapOverLazyValue() {
public void shouldMapLazyValue() {
final Lazy<Integer> testee = Lazy.of(() -> 42);
final Lazy<Integer> expected = Lazy.of(() -> 21);

assertThat(testee.map(i -> i / 2)).isEqualTo(expected);
}

// -- filter

@Test
public void shouldFilterOverLazyValue() {
final Lazy<Integer> testee = Lazy.of(() -> 42);
final Lazy<Option<Integer>> expectedPositive = Lazy.of(() -> Option.some(42));
final Lazy<Option<Integer>> expectedNegative = Lazy.of(Option::none);
final Lazy<Integer> expectedPositive = Lazy.of(() -> 42);
final Lazy<Integer> expectedNegative = Lazy.of(() -> -1);

assertThat(testee.filter(i -> i % 2 == 0)).isEqualTo(expectedPositive);
assertThat(testee.filter(i -> i % 2 != 0)).isEqualTo(expectedNegative);
assertThat(testee.filter(i -> i % 2 == 0, i -> -1)).isEqualTo(expectedPositive);
assertThat(testee.filter(i -> i % 2 != 0, i -> -1)).isEqualTo(expectedNegative);
}

@Test
Expand Down

0 comments on commit e01b510

Please sign in to comment.