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

com.github.tonivade.purefun.instances.EitherInstances 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.instances;

import static com.github.tonivade.purefun.Function1.cons;
import com.github.tonivade.purefun.Eq;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Witness;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.type.EitherOf;
import com.github.tonivade.purefun.type.Either_;
import com.github.tonivade.purefun.type.Eval;
import com.github.tonivade.purefun.type.EvalOf;
import com.github.tonivade.purefun.typeclasses.Applicative;
import com.github.tonivade.purefun.typeclasses.Bifunctor;
import com.github.tonivade.purefun.typeclasses.Foldable;
import com.github.tonivade.purefun.typeclasses.Functor;
import com.github.tonivade.purefun.typeclasses.Monad;
import com.github.tonivade.purefun.typeclasses.MonadError;
import com.github.tonivade.purefun.typeclasses.MonadThrow;
import com.github.tonivade.purefun.typeclasses.Traverse;

@SuppressWarnings("unchecked")
public interface EitherInstances {

  static  Eq, R>> eq(Eq leftEq, Eq rightEq) {
    return (a, b) -> {
      if (a instanceof Either.Left leftA && b instanceof Either.Left leftB) {
        return leftEq.eqv(leftA.getLeft(), leftB.getLeft());
      }
      if (a instanceof Either.Right rightA && b instanceof Either.Right rightB) {
        return rightEq.eqv(rightA.getRight(), rightB.getRight());
      }
      return false;
    };
  }

  static  Functor> functor() {
    return EitherFunctor.INSTANCE;
  }

  static Bifunctor bifunctor() {
    return EitherBifunctor.INSTANCE;
  }

  static  Applicative> applicative() {
    return EitherApplicative.INSTANCE;
  }

  static  Monad> monad() {
    return EitherMonad.INSTANCE;
  }

  static  MonadError, L> monadError() {
    return EitherMonadError.INSTANCE;
  }

  static MonadThrow> monadThrow() {
    return EitherMonadThrow.INSTANCE;
  }

  static  Foldable> foldable() {
    return EitherFoldable.INSTANCE;
  }

  static  Traverse> traverse() {
    return EitherTraverse.INSTANCE;
  }
}

interface EitherFunctor extends Functor> {

  @SuppressWarnings("rawtypes")
  EitherFunctor INSTANCE = new EitherFunctor() {};

  @Override
  default  Either map(Kind, ? extends T> value, Function1 map) {
    return EitherOf.narrowK(value).map(map);
  }
}

interface EitherBifunctor extends Bifunctor {

  EitherBifunctor INSTANCE = new EitherBifunctor() {};

  @Override
  default  Either bimap(Kind, ? extends B> value,
      Function1 leftMap, Function1 rightMap) {
    return EitherOf.narrowK(value).bimap(leftMap, rightMap);
  }
}

interface EitherPure extends Applicative> {

  @Override
  default  Either pure(T value) {
    return Either.right(value);
  }
}

interface EitherApplicative extends EitherPure {

  @SuppressWarnings("rawtypes")
  EitherApplicative INSTANCE = new EitherApplicative() {};

  @Override
  default  Either ap(Kind, ? extends T> value,
      Kind, ? extends Function1> apply) {
    return EitherOf.narrowK(value).flatMap(t -> EitherOf.narrowK(apply).map(f -> f.apply(t)));
  }
}

interface EitherMonad extends EitherPure, Monad> {

  @SuppressWarnings("rawtypes")
  EitherMonad INSTANCE = new EitherMonad() {};

  @Override
  default  Either flatMap(Kind, ? extends T> value,
      Function1, ? extends R>> map) {
    return EitherOf.narrowK(value).flatMap(map.andThen(EitherOf::narrowK));
  }
}

interface EitherMonadError extends EitherMonad, MonadError, L> {

  @SuppressWarnings("rawtypes")
  EitherMonadError INSTANCE = new EitherMonadError() {};

  @Override
  default  Either raiseError(L error) {
    return Either.left(error);
  }

  @Override
  default  Either handleErrorWith(Kind, A> value,
      Function1, ? extends A>> handler) {
    return EitherOf.narrowK(value).fold(handler.andThen(EitherOf::narrowK), Either::right);
  }
}

interface EitherMonadThrow extends EitherMonadError, MonadThrow> {

  EitherMonadThrow INSTANCE = new EitherMonadThrow() {};
}

interface EitherFoldable extends Foldable> {

  @SuppressWarnings("rawtypes")
  EitherFoldable INSTANCE = new EitherFoldable() {};

  @Override
  default  B foldLeft(Kind, ? extends A> value, B initial, Function2 mapper) {
    return EitherOf.narrowK(value).fold(cons(initial), a -> mapper.apply(initial, a));
  }

  @Override
  default  Eval foldRight(Kind, ? extends A> value, Eval initial,
      Function2, ? extends Eval> mapper) {
    return EitherOf.narrowK(value).fold(
        cons(initial).andThen(EvalOf::narrowK), 
        a -> mapper.andThen(EvalOf::narrowK).apply(a, initial));
  }
}

interface EitherTraverse extends Traverse>, EitherFoldable {

  @SuppressWarnings("rawtypes")
  EitherTraverse INSTANCE = new EitherTraverse() {};

  @Override
  default  Kind, R>> traverse(
      Applicative applicative, Kind, T> value,
      Function1> mapper) {
    return EitherOf.narrowK(value).fold(
      l -> applicative.pure(Either.left(l).kind()),
      t -> {
        Kind apply = mapper.apply(t);
        return applicative.map(apply, r -> Either.right(r));
      });
  }
}