[lazy] Add a lazy evaluator
This commit is contained in:
parent
bf7c55db0e
commit
9249ba8f59
5 changed files with 358 additions and 0 deletions
47
README.org
47
README.org
|
@ -25,8 +25,10 @@
|
||||||
- [Maybe]
|
- [Maybe]
|
||||||
- [Result]
|
- [Result]
|
||||||
- [Tree]
|
- [Tree]
|
||||||
|
- [Lazy]
|
||||||
- [Either]
|
- [Either]
|
||||||
|
|
||||||
|
|
||||||
** Maven Usage
|
** Maven Usage
|
||||||
|
|
||||||
#+BEGIN_SRC xml
|
#+BEGIN_SRC xml
|
||||||
|
@ -925,6 +927,51 @@
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
** Lazy
|
||||||
|
|
||||||
|
A Lazy evaluated expression. Using a Supplier to provide the value, only
|
||||||
|
evaluates the value when required, and never more than once.
|
||||||
|
|
||||||
|
*** Static Constructors
|
||||||
|
|
||||||
|
**** =static <R> Lazy<R> of(Supplier<R> supplier)=
|
||||||
|
|
||||||
|
Create a new Lazy value from the supplier.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Suppler<UUID> supplier = ...;
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
*** Instance Methods
|
||||||
|
|
||||||
|
**** =boolean isEvaluated()=
|
||||||
|
|
||||||
|
Checks if the value has been evaluated.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Lazy<UUID> lazy = ...;
|
||||||
|
final boolean isEvaluated = lazy.isEvaluated();
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
**** =T value()=
|
||||||
|
|
||||||
|
The value, evaluating it if necessary.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Lazy<UUID> lazy = ...;
|
||||||
|
final UUID value = lazy.value();
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
**** =<R> Lazy<R> map(Function<T, R> f)=
|
||||||
|
|
||||||
|
Maps the Lazy instance into a new Lazy instance using the function.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Lazy<UUID> uuidLazy = ...;
|
||||||
|
final Lazy<String> stringLazy = uuidLazy.map(v -> v.toString());
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
** Either
|
** Either
|
||||||
|
|
||||||
Allows handling a value that can be one of two types, a left value/type or a
|
Allows handling a value that can be one of two types, a left value/type or a
|
||||||
|
|
67
src/main/java/net/kemitix/mon/lazy/Lazy.java
Normal file
67
src/main/java/net/kemitix/mon/lazy/Lazy.java
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Paul Campbell
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies
|
||||||
|
* or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.kemitix.mon.lazy;
|
||||||
|
|
||||||
|
import net.kemitix.mon.Functor;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy evaluation of 'expensive' expressions.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the value
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
public interface Lazy<T> extends Functor<T, Lazy<?>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Lazy value from the supplier.
|
||||||
|
*
|
||||||
|
* @param supplier the source of the value
|
||||||
|
* @param <R> the type of the value
|
||||||
|
* @return a Lazy wrapper of the Supplier
|
||||||
|
*/
|
||||||
|
public static <R> Lazy<R> of(final Supplier<R> supplier) {
|
||||||
|
return new LazySupplier<>(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value has been evaluated.
|
||||||
|
*
|
||||||
|
* @return true if the value has been evaluated.
|
||||||
|
*/
|
||||||
|
public abstract boolean isEvaluated();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value, evaluating it if necessary.
|
||||||
|
*
|
||||||
|
* <p>Does not evaluate the value more than once.</p>
|
||||||
|
*
|
||||||
|
* @return the evaluated value
|
||||||
|
*/
|
||||||
|
public abstract T value();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract <R> Lazy<R> map(Function<T, R> f);
|
||||||
|
}
|
75
src/main/java/net/kemitix/mon/lazy/LazySupplier.java
Normal file
75
src/main/java/net/kemitix/mon/lazy/LazySupplier.java
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Paul Campbell
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies
|
||||||
|
* or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.kemitix.mon.lazy;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Lazy using a Supplier.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the value
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
class LazySupplier<T> implements Lazy<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
private final AtomicBoolean evaluated = new AtomicBoolean(false);
|
||||||
|
private final AtomicReference<T> value = new AtomicReference<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Lazy wrapper for the Supplier.
|
||||||
|
*
|
||||||
|
* @param supplier the source of the value
|
||||||
|
*/
|
||||||
|
LazySupplier(final Supplier<T> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEvaluated() {
|
||||||
|
return evaluated.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T value() {
|
||||||
|
if (evaluated.get()) {
|
||||||
|
return value.get();
|
||||||
|
}
|
||||||
|
synchronized (value) {
|
||||||
|
if (!evaluated.get()) {
|
||||||
|
value.set(supplier.get());
|
||||||
|
evaluated.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> Lazy<R> map(final Function<T, R> f) {
|
||||||
|
return Lazy.of(() -> f.apply(value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/main/java/net/kemitix/mon/lazy/package-info.java
Normal file
28
src/main/java/net/kemitix/mon/lazy/package-info.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Paul Campbell
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||||
|
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies
|
||||||
|
* or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deferred evaluation.
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.kemitix.mon.lazy;
|
141
src/test/java/net/kemitix/mon/lazy/LazySupplierTest.java
Normal file
141
src/test/java/net/kemitix/mon/lazy/LazySupplierTest.java
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package net.kemitix.mon.lazy;
|
||||||
|
|
||||||
|
import org.assertj.core.api.WithAssertions;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class LazySupplierTest implements WithAssertions {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateLazyThenSupplierIsNotCalled() {
|
||||||
|
//given
|
||||||
|
final AtomicBoolean supplierCalled = new AtomicBoolean(false);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalled.set(true);
|
||||||
|
return UUID.randomUUID();
|
||||||
|
};
|
||||||
|
//when
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//then
|
||||||
|
assertThat(supplierCalled).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenCreateLazyThenIsEvaluatedIsFalse() {
|
||||||
|
//given
|
||||||
|
final Supplier<UUID> supplier = UUID::randomUUID;
|
||||||
|
//when
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//then
|
||||||
|
assertThat(lazy.isEvaluated()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValueThenSupplierIsCalled() {
|
||||||
|
//given
|
||||||
|
final AtomicBoolean supplierCalled = new AtomicBoolean(false);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalled.set(true);
|
||||||
|
return UUID.randomUUID();
|
||||||
|
};
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//when
|
||||||
|
lazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(supplierCalled).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValueThenValueIsSameAsSupplier() {
|
||||||
|
//given
|
||||||
|
final UUID uuid = UUID.randomUUID();
|
||||||
|
final Supplier<UUID> supplier = () -> uuid;
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//when
|
||||||
|
final UUID value = lazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(value).isSameAs(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValueThenIsEvaluatedIsTrue() {
|
||||||
|
//given
|
||||||
|
final Supplier<UUID> supplier = () -> UUID.randomUUID();
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//when
|
||||||
|
lazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(lazy.isEvaluated()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValueCalledTwiceThenSupplierIsNotCalledAgain() {
|
||||||
|
//given
|
||||||
|
final AtomicInteger supplierCalledCounter = new AtomicInteger(0);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalledCounter.incrementAndGet();
|
||||||
|
return UUID.randomUUID();
|
||||||
|
};
|
||||||
|
final Lazy<UUID> lazy = Lazy.of(supplier);
|
||||||
|
//when
|
||||||
|
lazy.value();
|
||||||
|
lazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(supplierCalledCounter).hasValue(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenMapLazyThenSupplierNotCalled() {
|
||||||
|
//given
|
||||||
|
final UUID uuid = UUID.randomUUID();
|
||||||
|
final AtomicBoolean supplierCalled = new AtomicBoolean(false);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalled.set(true);
|
||||||
|
return uuid;
|
||||||
|
};
|
||||||
|
final Lazy<UUID> uuidLazy = Lazy.of(supplier);
|
||||||
|
//when
|
||||||
|
uuidLazy.map(UUID::toString);
|
||||||
|
//then
|
||||||
|
assertThat(supplierCalled).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenMapLazyValueThenSupplierIsCalled() {
|
||||||
|
//given
|
||||||
|
final UUID uuid = UUID.randomUUID();
|
||||||
|
final AtomicBoolean supplierCalled = new AtomicBoolean(false);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalled.set(true);
|
||||||
|
return uuid;
|
||||||
|
};
|
||||||
|
final Lazy<UUID> uuidLazy = Lazy.of(supplier);
|
||||||
|
final Lazy<String> stringLazy = uuidLazy.map(UUID::toString);
|
||||||
|
//when
|
||||||
|
stringLazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(supplierCalled).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenMapLazyValueThenValueIsCorrect() {
|
||||||
|
//given
|
||||||
|
final UUID uuid = UUID.randomUUID();
|
||||||
|
final AtomicBoolean supplierCalled = new AtomicBoolean(false);
|
||||||
|
final Supplier<UUID> supplier = () -> {
|
||||||
|
supplierCalled.set(true);
|
||||||
|
return uuid;
|
||||||
|
};
|
||||||
|
final Lazy<UUID> uuidLazy = Lazy.of(supplier);
|
||||||
|
final Lazy<String> stringLazy = uuidLazy.map(UUID::toString);
|
||||||
|
//when
|
||||||
|
final String value = stringLazy.value();
|
||||||
|
//then
|
||||||
|
assertThat(value).isEqualTo(uuid.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue