All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tonivade.purefun.effect.UIO Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2019, 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.Nothing.nothing;
import static java.util.Objects.requireNonNull;

import java.time.Duration;
import java.util.concurrent.Executor;

import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.Higher1;
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.Unit;
import com.github.tonivade.purefun.concurrent.Future;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.type.Try;
import com.github.tonivade.purefun.typeclasses.MonadDefer;

@HigherKind
public final class UIO implements Recoverable {

  private final ZIO value;

  UIO(ZIO value) {
    this.value = requireNonNull(value);
  }

  public T unsafeRunSync() {
    return value.provide(nothing()).get();
  }

  public Try safeRunSync() {
    return Try.of(this::unsafeRunSync);
  }

  @SuppressWarnings("unchecked")
  public  ZIO toZIO() {
    return (ZIO) value;
  }

  @SuppressWarnings("unchecked")
  public  EIO toEIO() {
    return new EIO<>((ZIO) value);
  }

  public Future toFuture() {
    return value.toFuture(nothing()).map(Either::get);
  }

  public void async(Executor executor, Consumer1> callback) {
    value.provideAsync(executor, nothing(), result -> callback.accept(result.map(Either::get)));
  }

  public void async(Consumer1> callback) {
    async(Future.DEFAULT_EXECUTOR, callback);
  }

  public  Higher1 foldMap(MonadDefer monad) {
    return monad.map(value.foldMap(nothing(), monad), Either::get);
  }

  public  UIO map(Function1 map) {
    return new UIO<>(value.map(map));
  }

  public  UIO flatMap(Function1> map) {
    return new UIO<>(value.flatMap(x -> map.apply(x).value));
  }

  public  UIO andThen(UIO next) {
    return new UIO<>(value.andThen(next.value));
  }

  public UIO recover(Function1 mapError) {
    return redeem(mapError, identity());
  }

  @SuppressWarnings("unchecked")
  public  UIO recoverWith(Class type, Function1 function) {
    return recover(cause -> {
      if (type.isAssignableFrom(cause.getClass())) {
        return function.apply((X) cause);
      }
      return sneakyThrow(cause);
    });
  }

  public  UIO redeem(Function1 mapError, Function1 map) {
    return redeemWith(mapError.andThen(UIO::pure), map.andThen(UIO::pure));
  }

  public  UIO redeemWith(Function1> mapError, Function1> map) {
    return new UIO<>(ZIO.redeem(value).foldM(error -> mapError.apply(error).value, x -> map.apply(x).value));
  }

  public UIO repeat() {
    return repeat(1);
  }

  public UIO repeat(int times) {
    return repeat(unit(), times);
  }

  public UIO repeat(Duration delay) {
    return repeat(delay, 1);
  }

  public UIO repeat(Duration delay, int times) {
    return repeat(sleep(delay), times);
  }

  public UIO retry() {
    return retry(1);
  }

  public UIO retry(int maxRetries) {
    return retry(unit(), maxRetries);
  }

  public UIO retry(Duration delay) {
    return retry(delay, 1);
  }

  public UIO retry(Duration delay, int maxRetries) {
    return retry(sleep(delay), maxRetries);
  }

  private UIO repeat(UIO pause, int times) {
    return redeemWith(UIO::raiseError, value -> {
      if (times > 0)
        return pause.andThen(repeat(pause, times - 1));
      else
        return pure(value);
    });
  }

  private UIO retry(UIO pause, int maxRetries) {
    return redeemWith(error -> {
      if (maxRetries > 0)
        return pause.andThen(retry(pause.repeat(), maxRetries - 1));
      else
        return raiseError(error);
    }, UIO::pure);
  }

  public static  UIO map2(UIO za, UIO zb, Function2 mapper) {
    return new UIO<>(ZIO.map2(za.value, zb.value, mapper));
  }

  public static UIO sleep(Duration delay) {
    return exec(() -> Thread.sleep(delay.toMillis()));
  }

  public static UIO exec(CheckedRunnable task) {
    return fold(ZIO.exec(task));
  }

  public static  UIO pure(A value) {
    return new UIO<>(ZIO.pure(value));
  }

  public static  UIO raiseError(Throwable throwable) {
    return new UIO<>(ZIO.fromEither(() -> { throw throwable; }));
  }

  public static  UIO defer(Producer> lazy) {
    return new UIO<>(ZIO.defer(() -> lazy.get().value));
  }

  public static  UIO task(Producer task) {
    return fold(ZIO.task(task));
  }

  public static  UIO bracket(UIO acquire, Function1> use) {
    return fold(ZIO.bracket(ZIO.redeem(acquire.value), resource -> ZIO.redeem(use.apply(resource).value)));
  }

  public static  UIO bracket(UIO acquire, Function1> use, Consumer1 release) {
    return fold(ZIO.bracket(ZIO.redeem(acquire.value), resource -> ZIO.redeem(use.apply(resource).value), release));
  }

  public static UIO unit() {
    return new UIO<>(ZIO.unit());
  }

  private static  UIO fold(ZIO zio) {
    return new UIO<>(zio.foldM(error -> UIO.raiseError(error).value, value -> UIO.pure(value).value));
  }
}