com.github.tonivade.purefun.effect.Schedule Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2022, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.effect;
import static com.github.tonivade.purefun.Function1.cons;
import static com.github.tonivade.purefun.Precondition.checkNonNull;
import java.time.Duration;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Operator1;
import com.github.tonivade.purefun.Tuple;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.data.ImmutableList;
import com.github.tonivade.purefun.data.Sequence;
import com.github.tonivade.purefun.type.Either;
@HigherKind
public sealed interface Schedule extends ScheduleOf {
Schedule map(Function1 super B, ? extends C> mapper);
Schedule contramap(Function1 super C, ? extends A> comap);
default Schedule dimap(
Function1 super C, ? extends A> comap, Function1 super B, ? extends D> map) {
return this.contramap(comap).map(map);
}
default Schedule as(C value) {
return map(ignore -> value);
}
default Schedule unit() {
return as(Unit.unit());
}
default Schedule andThen(Schedule next) {
return andThenEither(next).map(Either::merge);
}
Schedule> andThenEither(Schedule next);
Schedule> zip(Schedule other);
default Schedule zipLeft(Schedule other) {
return zip(other).map(Tuple2::get1);
}
default Schedule zipRight(Schedule other) {
return zip(other).map(Tuple2::get2);
}
Schedule compose(Schedule other);
default Schedule> collectAll() {
return this.>fold(ImmutableList.empty(), Sequence::append);
}
default Schedule fold(Z zero, Function2 next) {
return foldM(zero, (z, b) -> PureIO.pure(next.apply(z, b)));
}
Schedule foldM(Z zero, Function2> next);
default Schedule addDelay(Function1 map) {
return addDelayM(map.andThen(URIO::pure));
}
Schedule addDelayM(Function1> map);
default Schedule whileInput(Matcher1 condition) {
return whileInputM(condition.asFunction().andThen(UIO::pure));
}
default Schedule whileInputM(Function1> condition) {
return check((a, b) -> condition.apply(a));
}
default Schedule whileOutput(Matcher1 condition) {
return whileOutputM(condition.asFunction().andThen(UIO::pure));
}
default Schedule whileOutputM(Function1> condition) {
return check((a, b) -> condition.apply(b));
}
default Schedule untilInput(Matcher1 condition) {
return untilInputM(condition.asFunction().andThen(UIO::pure));
}
Schedule untilInputM(Function1> condition);
default Schedule untilOutput(Matcher1 condition) {
return untilOutputM(condition.asFunction().andThen(UIO::pure));
}
Schedule untilOutputM(Function1> condition);
Schedule check(Function2> condition);
public static Schedule once() {
return Schedule.recurs(1).unit();
}
static Schedule recurs(int times) {
return Schedule.forever().whileOutput(x -> x < times);
}
static Schedule spaced(Duration delay) {
return Schedule.forever().addDelay(cons(delay));
}
static Schedule linear(Duration delay) {
return delayed(Schedule.forever().map(i -> delay.multipliedBy(i + 1L)));
}
static Schedule exponential(Duration delay) {
return exponential(delay, 2.0);
}
static Schedule exponential(Duration delay, double factor) {
return delayed(Schedule.forever().map(i -> delay.multipliedBy((long) Math.pow(factor, i.doubleValue()))));
}
static Schedule delayed(Schedule schedule) {
return schedule.addDelay(x -> x);
}
static Schedule> recursSpaced(Duration delay, int times) {
return Schedule.recurs(times).zip(Schedule.spaced(delay));
}
static Schedule never() {
return ScheduleImpl.of(
URIO.unit(),
(a, s) -> PureIO.raiseError(Unit.unit()),
(a, s) -> s);
}
static Schedule forever() {
return unfold(0, a -> a + 1);
}
static Schedule succeed(B value) {
return Schedule.forever().as(value);
}
static Schedule identity() {
return ScheduleImpl.of(URIO.unit(),
(a, s) -> PureIO.unit(),
(a, s) -> a);
}
static Schedule doWhile(Matcher1 condition) {
return doWhileM(condition.asFunction().andThen(UIO::pure));
}
static Schedule doWhileM(Function1> condition) {
return Schedule.identity().whileInputM(condition);
}
static Schedule doUntil(Matcher1 condition) {
return doUntilM(condition.asFunction().andThen(UIO::pure));
}
static Schedule doUntilM(Function1> condition) {
return Schedule.identity().untilInputM(condition);
}
static Schedule unfold(B initial, Operator1 next) {
return unfoldM(URIO.pure(initial), next.andThen(PureIO::pure));
}
static Schedule unfoldM(
URIO initial, Function1> next) {
return ScheduleImpl.of(initial, (a, s) -> next.apply(s), (a, s) -> s);
}
@FunctionalInterface
interface Update {
PureIO update(A last, S state);
}
@FunctionalInterface
interface Extract {
B extract(A last, S state);
}
}
final class ScheduleImpl implements Schedule, Schedule.Update, Schedule.Extract {
private final URIO initial;
private final Update update;
private final Extract extract;
private ScheduleImpl(
URIO initial,
Update update,
Extract extract) {
this.initial = checkNonNull(initial);
this.update = checkNonNull(update);
this.extract = checkNonNull(extract);
}
public URIO initial() {
return initial;
}
@Override
public PureIO update(A last, S state) {
return update.update(last, state);
}
@Override
public B extract(A last, S state) {
return extract.extract(last, state);
}
@Override
public Schedule map(Function1 super B, ? extends C> mapper) {
return ScheduleImpl.of(
initial,
update,
(a, s) -> mapper.apply(extract(a, s)));
}
@Override
public Schedule contramap(Function1 super C, ? extends A> comap) {
return ScheduleImpl.of(
initial,
(c, s) -> update(comap.apply(c), s),
(c, s) -> extract(comap.apply(c), s));
}
@Override
public Schedule andThen(Schedule next) {
return andThenEither(next).map(Either::merge);
}
public Schedule> andThenEither(Schedule next) {
return doAndThenEither((ScheduleImpl) next);
}
@Override
public Schedule> zip(Schedule other) {
return doZip((ScheduleImpl) other);
}
@Override
public Schedule compose(Schedule other) {
return doCompose((ScheduleImpl) other);
}
@Override
public Schedule foldM(Z zero, Function2> next) {
return ScheduleImpl.of(
initial.map(s -> Tuple.of(s, zero)),
(a, sz) -> {
PureIO update = update(a, sz.get1());
PureIO other = next.apply(sz.get2(), extract(a, sz.get1()));
return update.zip(other);
},
(a, sz) -> sz.get2());
}
@Override
public Schedule addDelayM(Function1> map) {
return updated(u -> (a, s) -> {
PureIO> map2 =
PureIO.parMap2(
map.apply(extract(a, s)).toPureIO(),
u.update(a, s),
Tuple::of);
return map2.flatMap(ds -> {
PureIO sleep = URIO.sleep(ds.get1()).toPureIO();
return sleep.map(ignore -> ds.get2());
});
});
}
@Override
public Schedule untilInputM(Function1> condition) {
return updated(u -> (a, s) -> {
UIO apply = condition.apply(a);
return apply.toPureIO()
.flatMap(test -> test ? PureIO.raiseError(Unit.unit()) : update(a, s));
});
}
@Override
public Schedule untilOutputM(Function1> condition) {
return updated(u -> (a, s) -> {
UIO apply = condition.apply(extract(a, s));
return apply.toPureIO()
.flatMap(test -> test ? PureIO.raiseError(Unit.unit()) : update(a, s));
});
}
@Override
public Schedule check(Function2> condition) {
return updated(u -> (a, s) -> {
PureIO apply = condition.apply(a, this.extract(a, s)).toPureIO();
return apply.flatMap(result -> result != null && result ? u.update(a, s) : PureIO.raiseError(Unit.unit()));
});
}
private ScheduleImpl, A, Either> doAndThenEither(ScheduleImpl other) {
return ScheduleImpl., A, Either>of(
initial.map(Either::left),
(a, st) -> st.fold(
s -> {
PureIO> orElse =
other.initial.toPureIO().flatMap(t -> other.update(a, t).map(Either::right));
return this.update(a, s).map(Either::left).orElse(orElse);
},
t -> other.update(a, t).map(Either::right)),
(a, st) -> st.fold(
s -> Either.left(this.extract(a, s)),
t -> Either.right(other.extract(a, t))));
}
private ScheduleImpl, A, Tuple2> doZip(ScheduleImpl other) {
return ScheduleImpl., A, Tuple2>of(
this.initial.zip(other.initial),
(a, st) -> {
PureIO self = this.update(a, st.get1());
PureIO next = other.update(a, st.get2());
return self.zip(next);
},
(a, st) -> Tuple.of(
this.extract(a, st.get1()),
other.extract(a, st.get2())));
}
private ScheduleImpl, A, C> doCompose(ScheduleImpl other) {
return ScheduleImpl., A, C>of(
this.initial.zip(other.initial),
(a, st) -> {
PureIO self = this.update(a, st.get1());
PureIO next = other.update(this.extract(a, st.get1()), st.get2());
return self.zip(next);
},
(a, st) -> other.extract(this.extract(a, st.get1()), st.get2()));
}
private ScheduleImpl updated(Function1, Update> update) {
return ScheduleImpl.of(initial, update.apply(this.update), this.extract);
}
public static ScheduleImpl of(
URIO initial,
Update update,
Extract extract) {
return new ScheduleImpl<>(initial, update, extract);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy