/* __ __ __ __ __ ___
* \ \ / / \ \ / / __/
* \ \/ / /\ \ \/ / /
* \____/__/ \__\____/__/.ɪᴏ
* ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
*/
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;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Represents a lazy evaluated value. Compared to a Supplier, Lazy is memoizing, i.e. it evaluates only once and
* therefore is referential transparent.
*
* <pre>
* <code>
* final Lazy<Double> l = Lazy.of(Math::random);
* l.isEvaluated(); // = false
* l.get(); // = 0.123 (random generated)
* l.isEvaluated(); // = true
* l.get(); // = 0.123 (memoized)
* </code>
* </pre>
*
* Example of creating a <em>real</em> lazy value (works only with interfaces):
*
* <pre><code>final CharSequence chars = Lazy.val(() -> "Yay!", CharSequence.class);</code></pre>
*
* @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;
// read http://javarevisited.blogspot.de/2014/05/double-checked-locking-on-singleton-in-java.html
private transient volatile Supplier<? extends T> supplier;
private T value; // will behave as a volatile in reality, because a supplier volatile read will update all fields (see https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile)
// should not be called directly
private Lazy(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
/**
* Narrows a widened {@code Lazy<? extends T>} to {@code Lazy<T>}
* by performing a type-safe cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param lazy A {@code Lazy}.
* @param <T> Component type of the {@code Lazy}.
* @return the given {@code lazy} instance as narrowed type {@code Lazy<T>}.
*/
@SuppressWarnings("unchecked")
public static <T> Lazy<T> narrow(Lazy<? extends T> lazy) {
return (Lazy<T>) lazy;
}
/**
* Creates a {@code Lazy} that requests its value from a given {@code Supplier}. The supplier is asked only once,
* the value is memoized.
*
* @param <T> type of the lazy value
* @param supplier A supplier
* @return A new instance of Lazy
*/
@SuppressWarnings("unchecked")
public static <T> Lazy<T> of(Supplier<? extends T> supplier) {
Objects.requireNonNull(supplier, "supplier is null");
if (supplier instanceof Lazy) {
return (Lazy<T>) supplier;
} else {
return new Lazy<>(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
*/
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::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.
*
* @param supplier A supplier
* @param type An interface
* @param <T> type of the lazy value
* @return A new instance of T
*/
@GwtIncompatible("reflection is not supported")
@SuppressWarnings("unchecked")
public static <T> T val(Supplier<? extends T> supplier, Class<T> type) {
Objects.requireNonNull(supplier, "supplier is null");
Objects.requireNonNull(type, "type is null");
if (!type.isInterface()) {
throw new IllegalArgumentException("type has to be an interface");
}
final Lazy<T> lazy = Lazy.of(supplier);
final InvocationHandler handler = (proxy, method, args) -> method.invoke(lazy.get(), args);
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] { type }, handler);
}
public Option<T> filter(Predicate<? super T> predicate) {
final T v = get();
return predicate.test(v) ? Option.some(v) : Option.none();
}
/**
* Evaluates this lazy value and caches it, when called the first time.
* On subsequent calls, returns the cached value.
*
* @return the lazy evaluated value
*/
@Override
public T get() {
return (supplier == null) ? value : computeValue();
}
private synchronized T computeValue() {
final Supplier<? extends T> s = supplier;
if (s != null) {
value = s.get();
supplier = null;
}
return value;
}
/**
* A {@code Lazy}'s value is computed synchronously.
*
* @return false
*/
@Override
public boolean isAsync() {
return false;
}
@Override
public boolean isEmpty() {
return false;
}
/**
* Checks, if this lazy value is evaluated.
* <p>
* Note: A value is internally evaluated (once) by calling {@link #get()}.
*
* @return true, if the value is evaluated, false otherwise.
* @throws UnsupportedOperationException if this value is undefined
*/
public boolean isEvaluated() {
return supplier == null;
}
/**
* A {@code Lazy}'s value is computed lazily.
*
* @return true
*/
@Override
public boolean isLazy() {
return true;
}
@Override
public boolean isSingleValued() {
return true;
}
@Override
public Iterator<T> iterator() {
return Iterator.of(get());
}
@Override
public <U> Lazy<U> map(Function<? super T, ? extends U> mapper) {
return Lazy.of(() -> mapper.apply(get()));
}
@Override
public Lazy<T> peek(Consumer<? super T> action) {
action.accept(get());
return this;
}
/**
* Transforms this {@code Lazy}.
*
* @param f A transformation
* @param <U> Type of transformation result
* @return An instance of type {@code U}
* @throws NullPointerException if {@code f} is null
*/
public <U> U transform(Function<? super Lazy<T>, ? extends U> f) {
Objects.requireNonNull(f, "f is null");
return f.apply(this);
}
@Override
public String stringPrefix() {
return "Lazy";
}
@Override
public boolean equals(Object o) {
return (o == this) || (o instanceof Lazy && Objects.equals(((Lazy<?>) o).get(), get()));
}
@Override
public int hashCode() {
return Objects.hash(get());
}
@Override
public String toString() {
return stringPrefix() + "(" + (!isEvaluated() ? "?" : value) + ")";
}
/**
* Ensures that the value is evaluated before serialization.
*
* @param s An object serialization stream.
* @throws java.io.IOException If an error occurs writing to the stream.
*/
@GwtIncompatible("The Java serialization protocol is explicitly not supported")
private void writeObject(ObjectOutputStream s) throws IOException {
get(); // evaluates the lazy value if it isn't evaluated yet!
s.defaultWriteObject();
}
}