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

com.github.tonivade.purefun.free.Free Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2020, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.purefun.free;

import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Higher1;
import com.github.tonivade.purefun.Higher2;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Instance;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.typeclasses.Functor;
import com.github.tonivade.purefun.typeclasses.InjectK;
import com.github.tonivade.purefun.typeclasses.Monad;
import com.github.tonivade.purefun.typeclasses.FunctionK;

import static com.github.tonivade.purefun.Unit.unit;
import static java.util.Objects.requireNonNull;

@HigherKind
public abstract class Free {

  private Free() {}

  public static  Free pure(T value) {
    return new Pure<>(value);
  }

  public static  Free liftF(Higher1 value) {
    return new Suspend<>(value);
  }

  public static  Free inject(InjectK inject, Higher1 value) {
    return liftF(inject.inject(value));
  }

  public static  Free defer(Producer> value) {
    Free pure = pure(unit());
    return pure.flatMap(ignore -> value.get());
  }

  public static  Monad> monadF() {
    return FreeMonad.instance();
  }

  public static  FunctionK> functionKF(FunctionK functionK) {
    return new FunctionK>() {
      @Override
      public  Higher2 apply(Higher1 from) {
        return liftF(functionK.apply(from)).kind2();
      }
    };
  }

  public  Free map(Function1 map) {
    return flatMap(map.andThen(Free::pure));
  }

  public abstract  Free flatMap(Function1> map);

  public abstract Either>, A> resume(Functor functor);

  public abstract Free step();

  public  Free andThen(Free next) {
    return flatMap(ignore -> next);
  }

  public  Higher1 foldMap(Monad monad, FunctionK interpreter) {
    return monad.tailRecM(this, value -> value.step().foldStep(monad, interpreter));
  }

  protected abstract  Higher1, A>> foldStep(Monad monad, FunctionK interpreter);

  public static final class Pure extends Free {

    private final A value;

    private Pure(A value) {
      this.value = requireNonNull(value);
    }

    @Override
    public  Free flatMap(Function1> map) {
      return new FlatMapped<>(this, map);
    }

    @Override
    public Either>, A> resume(Functor functor) {
      return Either.right(value);
    }

    @Override
    public Free step() {
      return this;
    }

    @Override
    protected  Higher1, A>> foldStep(Monad monad, FunctionK interpreter) {
      return monad.pure(Either.right(value));
    }
  }

  public static final class Suspend extends Free {

    private final Higher1 value;

    private Suspend(Higher1 value) {
      this.value = requireNonNull(value);
    }

    @Override
    public  Free flatMap(Function1> map) {
      return new FlatMapped<>(this, map);
    }

    @Override
    public Either>, A> resume(Functor functor) {
      return Either.left(functor.map(value, Free::pure));
    }

    @Override
    public Free step() {
      return this;
    }

    @Override
    protected  Higher1, A>> foldStep(Monad monad, FunctionK interpreter) {
      return monad.map(interpreter.apply(value), Either::right);
    }
  }

  public static final class FlatMapped extends Free {

    private final Free value;
    private final Function1> next;

    private FlatMapped(Free value, Function1> next) {
      this.value = requireNonNull(value);
      this.next = requireNonNull(next);
    }

    @Override
    public  Free flatMap(Function1> map) {
      return new FlatMapped<>(value, free -> new FlatMapped<>(next.apply(free), map));
    }

    @Override
    public Either>, B> resume(Functor functor) {
      if (value instanceof Free.Suspend) {
        Free.Suspend suspend = (Free.Suspend) value;
        return Either.left(functor.map(suspend.value, next));
      }
      if (value instanceof Free.Pure) {
        Free.Pure pure = (Free.Pure) value;
        return next.apply(pure.value).resume(functor);
      }
      Free.FlatMapped flatMapped = (Free.FlatMapped) value;
      return flatMapped.value.flatMap(x -> flatMapped.next.apply(x).flatMap(next)).resume(functor);
    }

    @Override
    public Free step() {
      if (value instanceof FlatMapped) {
        Free.FlatMapped flatMapped = (Free.FlatMapped) value;
        return flatMapped.value.flatMap(x -> flatMapped.next.apply(x).flatMap(next)).step();
      }
      if (value instanceof Pure) {
        Free.Pure pure = (Free.Pure) value;
        return next.apply(pure.value).step();
      }
      return this;
    }

    @Override
    protected  Higher1, B>> foldStep(Monad monad, FunctionK interpreter) {
      return monad.map(value.foldMap(monad, interpreter), next.andThen(Either::left));
    }
  }
}

@Instance
interface FreeMonad extends Monad> {

  @Override
  default  Higher2 pure(T value) {
    return Free.pure(value).kind2();
  }

  @Override
  default  Higher2 flatMap(
      Higher1, T> value, Function1, R>> map) {
    Free free = value.fix1(Free::narrowK);
    return free.flatMap(map.andThen(Free::narrowK)).kind2();
  }
}