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();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy