package bjc.utils.data.experimental; import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.IFunctionalList; /** * A holder that holds a means to create a value, but doesn't actually * compute the value until it's needed * * @author ben * * @param */ public class Lazy implements IHolder { private static class BoundLazy implements IHolder { private Supplier> oldSupplier; private Function> binder; private IHolder boundHolder; private boolean holderBound; private IFunctionalList> actions = new FunctionalList<>(); public BoundLazy(Supplier> supp, Function> binder) { oldSupplier = supp; this.binder = binder; } @Override public IHolder bind( Function> bindr) { IFunctionalList> pendingActions = new FunctionalList<>(); actions.forEach(pendingActions::add); Supplier> typeSupplier = () -> { IHolder oldHolder = boundHolder; if (!holderBound) { oldHolder = oldSupplier.get().unwrap(binder); } return pendingActions.reduceAux(oldHolder, (action, state) -> { return state.transform(action); }, (value) -> value); }; return new BoundLazy<>(typeSupplier, bindr); } @Override public IHolder map( Function mapper) { IFunctionalList> pendingActions = new FunctionalList<>(); actions.forEach(pendingActions::add); Supplier typeSupplier = () -> { IHolder oldHolder = boundHolder; if (!holderBound) { oldHolder = oldSupplier.get().unwrap(binder); } return pendingActions.reduceAux(oldHolder.getValue(), (action, state) -> { return action.apply(state); }, (value) -> mapper.apply(value)); }; return new Lazy<>(typeSupplier); } @Override public IHolder transform( UnaryOperator transformer) { actions.add(transformer); return this; } @Override public UnwrappedType unwrap( Function unwrapper) { if (!holderBound) { boundHolder = oldSupplier.get().unwrap(binder::apply); } return boundHolder.unwrap(unwrapper); } @Override public String toString() { if (holderBound) { return boundHolder.toString(); } return "(unmaterialized)"; } } private Supplier valueSupplier; private IFunctionalList> actions = new FunctionalList<>(); private boolean valueMaterialized; private ContainedType heldValue; /** * Create a new lazy value from the specified seed value * * @param value * The seed value to use */ public Lazy(ContainedType value) { heldValue = value; valueMaterialized = true; } /** * Create a new lazy value from the specified value source * * @param supp * The source of a value to use */ public Lazy(Supplier supp) { valueSupplier = supp; valueMaterialized = false; } private Lazy(Supplier supp, IFunctionalList> pendingActions) { valueSupplier = supp; actions = pendingActions; } @Override public IHolder bind( Function> binder) { IFunctionalList> pendingActions = new FunctionalList<>(); actions.forEach(pendingActions::add); Supplier supplier = () -> { if (valueMaterialized) { return heldValue; } return valueSupplier.get(); }; return new BoundLazy<>(() -> { return new Lazy<>(supplier, pendingActions); }, binder); } @Override public IHolder map( Function mapper) { IFunctionalList> pendingActions = new FunctionalList<>(); actions.forEach(pendingActions::add); return new Lazy<>(() -> { ContainedType currVal = heldValue; if (!valueMaterialized) { currVal = valueSupplier.get(); } return pendingActions.reduceAux(currVal, UnaryOperator::apply, (value) -> mapper.apply(value)); }); } @Override public IHolder transform( UnaryOperator transformer) { actions.add(transformer); return this; } @Override public UnwrappedType unwrap( Function unwrapper) { if (!valueMaterialized) { heldValue = valueSupplier.get(); valueMaterialized = true; } actions.forEach((action) -> { heldValue = action.apply(heldValue); }); actions = new FunctionalList<>(); return unwrapper.apply(heldValue); } @Override public String toString() { if (valueMaterialized) { if (actions.isEmpty()) { return "value[v='" + heldValue + "']"; } return "value[v='" + heldValue + "'] (has pending transforms)"; } return "(unmaterialized)"; } }