com.github.tonivade.purefun.effect.UIO 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.identity;
import static com.github.tonivade.purefun.Function2.first;
import static com.github.tonivade.purefun.Function2.second;
import static com.github.tonivade.purefun.Nothing.nothing;
import static com.github.tonivade.purefun.Precondition.checkNonNull;
import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.Effect;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Nothing;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Recoverable;
import com.github.tonivade.purefun.Tuple;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.concurrent.Future;
import com.github.tonivade.purefun.concurrent.Promise;
import com.github.tonivade.purefun.data.ImmutableList;
import com.github.tonivade.purefun.data.ImmutableMap;
import com.github.tonivade.purefun.data.Sequence;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.Try;
import com.github.tonivade.purefun.typeclasses.Fiber;
import com.github.tonivade.purefun.typeclasses.FunctionK;
@HigherKind
public final class UIO implements UIOOf, Effect, Recoverable {
private static final UIO UNIT = new UIO<>(PureIO.unit());
private final PureIO instance;
UIO(PureIO value) {
this.instance = checkNonNull(value);
}
public Future runAsync() {
return instance.runAsync(nothing()).map(Either::getRight);
}
public Future runAsync(Executor executor) {
return UIO.forked(executor).andThen(this).runAsync();
}
public A unsafeRunSync() {
return instance.provide(nothing()).get();
}
public Try safeRunSync() {
return Try.of(this::unsafeRunSync);
}
@SuppressWarnings("unchecked")
public PureIO toPureIO() {
return (PureIO) instance;
}
@SuppressWarnings("unchecked")
public EIO toEIO() {
return new EIO<>((PureIO) instance);
}
@SuppressWarnings("unchecked")
public RIO toRIO() {
return new RIO<>((PureIO) PureIO.redeem(instance));
}
@SuppressWarnings("unchecked")
public URIO toURIO() {
return new URIO<>((PureIO) instance);
}
public Task toTask() {
return new Task<>(PureIO.redeem(instance));
}
public void safeRunAsync(Consumer1 super Try extends A>> callback) {
safeRunAsync(Future.DEFAULT_EXECUTOR, callback);
}
public void safeRunAsync(Executor executor, Consumer1 super Try extends A>> callback) {
instance.provideAsync(nothing(), executor, x -> callback.accept(x.map(Either::getRight)));
}
@Override
public UIO map(Function1 super A, ? extends B> map) {
return new UIO<>(instance.map(map));
}
@Override
public UIO flatMap(Function1 super A, ? extends Kind> map) {
return new UIO<>(instance.flatMap(x -> {
UIO extends B> apply = map.andThen(UIOOf::narrowK).apply(x);
return apply.instance;
}));
}
@Override
public UIO andThen(Kind next) {
return new UIO<>(instance.andThen(next.fix(UIOOf.toUIO()).instance));
}
@Override
public UIO ap(Kind> apply) {
return new UIO<>(instance.ap(apply.fix(UIOOf.toUIO()).instance));
}
public UIO recover(Function1 super Throwable, ? extends A> mapError) {
return redeem(mapError, identity());
}
@SuppressWarnings("unchecked")
public UIO recoverWith(Class type, Function1 super X, ? extends A> function) {
return recover(cause -> {
if (type.isAssignableFrom(cause.getClass())) {
return function.apply((X) cause);
}
return sneakyThrow(cause);
});
}
public UIO redeem(
Function1 super Throwable, ? extends B> mapError, Function1 super A, ? extends B> map) {
return redeemWith(mapError.andThen(UIO::pure), map.andThen(UIO::pure));
}
public UIO redeemWith(
Function1 super Throwable, ? extends Kind> mapError,
Function1 super A, ? extends Kind> map) {
return new UIO<>(PureIO.redeem(instance).foldM(
error -> mapError.andThen(UIOOf::narrowK).apply(error).instance,
value -> map.andThen(UIOOf::narrowK).apply(value).instance));
}
@Override
public UIO> zip(Kind other) {
return zipWith(other, Tuple::of);
}
@Override
public UIO zipLeft(Kind other) {
return zipWith(other, first());
}
@Override
public UIO zipRight(Kind other) {
return zipWith(other, second());
}
@Override
public UIO zipWith(Kind other,
Function2 super A, ? super B, ? extends C> mapper) {
return parMap2(this, other.fix(UIOOf.toUIO()), mapper);
}
public UIO> fork() {
return new UIO<>(instance.fork().map(f -> f.mapK(new FunctionK, Nothing>, UIO_>() {
@Override
public UIO apply(Kind, Nothing>, ? extends T> from) {
return new UIO<>(from.fix(PureIOOf::narrowK));
}
})));
}
public UIO timeout(Duration duration) {
return timeout(Future.DEFAULT_EXECUTOR, duration);
}
public UIO timeout(Executor executor, Duration duration) {
return racePair(executor, this, sleep(duration)).flatMap(either -> either.fold(
ta -> ta.get2().cancel().fix(UIOOf.toUIO()).map(x -> ta.get1()),
tb -> tb.get1().cancel().fix(UIOOf.toUIO()).flatMap(x -> UIO.raiseError(new TimeoutException()))));
}
@Override
public UIO repeat() {
return repeat(1);
}
@Override
public UIO repeat(int times) {
return fold(PureIO.redeem(instance).repeat(times));
}
@Override
public UIO repeat(Duration delay) {
return repeat(delay, 1);
}
@Override
public UIO repeat(Duration delay, int times) {
return fold(PureIO.redeem(instance).repeat(delay, times));
}
public UIO repeat(Schedule schedule) {
return fold(PureIO.redeem(instance).repeat(schedule));
}
@Override
public UIO retry() {
return retry(1);
}
@Override
public UIO retry(int maxRetries) {
return retry(Schedule.recurs(maxRetries));
}
@Override
public UIO retry(Duration delay) {
return retry(delay, 1);
}
@Override
public UIO retry(Duration delay, int maxRetries) {
return retry(Schedule.recursSpaced(delay, maxRetries));
}
public UIO retry(Schedule schedule) {
return fold(PureIO.redeem(instance).retry(schedule));
}
@Override
public UIO> timed() {
return new UIO<>(instance.timed());
}
public static UIO forked(Executor executor) {
return async(callback -> executor.execute(() -> callback.accept(Try.success(Unit.unit()))));
}
public static UIO parMap2(Kind za, Kind zb,
Function2 super A, ? super B, ? extends C> mapper) {
return parMap2(Future.DEFAULT_EXECUTOR, za, zb, mapper);
}
public static UIO parMap2(Executor executor, Kind za, Kind zb,
Function2 super A, ? super B, ? extends C> mapper) {
return new UIO<>(PureIO.parMap2(executor, za.fix(UIOOf::narrowK).instance, zb.fix(UIOOf::narrowK).instance, mapper));
}
public static UIO> race(Kind fa, Kind fb) {
return race(Future.DEFAULT_EXECUTOR, fa, fb);
}
public static UIO> race(Executor executor, Kind fa, Kind fb) {
return racePair(executor, fa, fb).flatMap(either -> either.fold(
ta -> ta.get2().cancel().fix(UIOOf.toUIO()).map(x -> Either.left(ta.get1())),
tb -> tb.get1().cancel().fix(UIOOf.toUIO()).map(x -> Either.right(tb.get2()))));
}
public static UIO>, Tuple2, B>>>
racePair(Executor executor, Kind fa, Kind fb) {
PureIO instance1 = fa.fix(UIOOf.toUIO()).instance.fix(PureIOOf::narrowK);
PureIO instance2 = fb.fix(UIOOf.toUIO()).instance.fix(PureIOOf::narrowK);
return new UIO<>(PureIO.racePair(executor, instance1, instance2).map(
either -> either.bimap(a -> a.map2(f -> f.mapK(new FunctionK, Nothing>, UIO_>() {
@Override
public UIO apply(Kind, Nothing>, ? extends T> from) {
return new UIO<>(from.fix(PureIOOf::narrowK));
}
})), b -> b.map1(f -> f.mapK(new FunctionK, Nothing>, UIO_>() {
@Override
public UIO apply(Kind, Nothing>, ? extends T> from) {
return new UIO<>(from.fix(PureIOOf::narrowK));
}
})))));
}
public static Function1> lift(Function1 super A, ? extends B> function) {
return value -> task(() -> function.apply(value));
}
public static Function1> liftOption(Function1 super A, ? extends Option extends B>> function) {
return value -> fromOption(function.apply(value));
}
public static Function1> liftTry(Function1 super A, ? extends Try extends B>> function) {
return value -> fromTry(function.apply(value));
}
public static Function1> liftEither(Function1 super A, ? extends Either> function) {
return value -> fromEither(function.apply(value));
}
public static UIO sleep(Duration delay) {
return sleep(Future.DEFAULT_EXECUTOR, delay);
}
public static UIO sleep(Executor executor, Duration delay) {
return fold(PureIO.sleep(executor, delay));
}
public static UIO exec(CheckedRunnable task) {
return fold(PureIO.exec(task));
}
public static UIO pure(A value) {
return new UIO<>(PureIO.pure(value));
}
public static UIO raiseError(Throwable throwable) {
return new UIO<>(PureIO.fromEither(() -> { throw throwable; }));
}
public static UIO defer(Producer> lazy) {
return new UIO<>(PureIO.defer(() -> lazy.andThen(UIOOf::narrowK).get().instance));
}
public static UIO task(Producer extends A> task) {
return fold(PureIO.task(task));
}
public static UIO fromOption(Option extends T> task) {
return fromEither(task.toEither());
}
public static UIO fromTry(Try extends T> task) {
return fromEither(task.toEither());
}
public static UIO fromEither(Either task) {
return task.fold(UIO::raiseError, UIO::pure);
}
public static UIO fromPromise(Promise extends T> promise) {
Consumer1>> consumer = promise::onComplete;
return async(consumer);
}
public static UIO never() {
return async(cb -> {});
}
public static UIO async(Consumer1>> consumer) {
return fold(PureIO.async(
(env, cb1) -> consumer.accept(result -> cb1.accept(result.map(Either::right)))));
}
public static UIO cancellable(Function1>, UIO> consumer) {
return fold(PureIO.cancellable(
(env, cb1) -> consumer.andThen(UIO::toPureIO).apply(result -> cb1.accept(result.map(Either::right)))));
}
public static UIO>> memoize(Function1> function) {
return memoize(Future.DEFAULT_EXECUTOR, function);
}
public static UIO>> memoize(Executor executor, Function1> function) {
var ref = Ref.make(ImmutableMap.>empty());
return ref.map(r -> {
Function1>> result = a -> r.modify(map -> map.get(a).fold(() -> {
Promise promise = Promise.make();
function.apply(a).safeRunAsync(executor, promise::tryComplete);
return Tuple.of(UIO.fromPromise(promise), map.put(a, promise));
}, promise -> Tuple.of(UIO.fromPromise(promise), map)));
return result.andThen(io -> io.flatMap(identity()));
});
}
public static UIO> traverse(Sequence extends UIO> sequence) {
return traverse(Future.DEFAULT_EXECUTOR, sequence);
}
public static UIO> traverse(Executor executor, Sequence extends UIO> sequence) {
return sequence.foldLeft(pure(ImmutableList.empty()),
(UIO> xs, UIO a) -> parMap2(executor, xs, a, Sequence::append));
}
public static UIO bracket(
Kind acquire, Function1 super A, ? extends Kind> use) {
return fold(PureIO.bracket(PureIO.redeem(acquire.fix(UIOOf::narrowK).instance),
resource -> PureIO.redeem(use.andThen(UIOOf::narrowK).apply(resource).instance)));
}
public static UIO bracket(Kind acquire,
Function1 super A, ? extends Kind> use, Consumer1 super A> release) {
return fold(PureIO.bracket(PureIO.redeem(acquire.fix(UIOOf::narrowK).instance),
resource -> PureIO.redeem(use.andThen(UIOOf::narrowK).apply(resource).instance), release));
}
public static UIO bracket(Kind acquire,
Function1 super A, ? extends Kind> use, Function1 super A, ? extends Kind> release) {
return fold(PureIO.bracket(PureIO.redeem(acquire.fix(UIOOf::narrowK).instance),
resource -> PureIO.redeem(use.andThen(UIOOf::narrowK).apply(resource).instance), release.andThen(UIOOf::narrowK).andThen(UIO::toPureIO)));
}
public static UIO unit() {
return UNIT;
}
private static UIO fold(PureIO zio) {
return new UIO<>(zio.foldM(error -> UIO.raiseError(error).instance, value -> UIO.pure(value).instance));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy