com.github.tonivade.purefun.free.FreeAp 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.type.Const;
import com.github.tonivade.purefun.typeclasses.Applicative;
import com.github.tonivade.purefun.typeclasses.FunctionK;
import com.github.tonivade.purefun.typeclasses.Monoid;
import java.util.Deque;
import java.util.LinkedList;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
@HigherKind
public abstract class FreeAp {
private FreeAp() {}
public abstract FreeAp map(Function1 mapper);
public FreeAp ap(FreeAp> apply) {
if (apply instanceof Pure) {
Pure> pure = (Pure>) apply;
return map(pure.value);
}
return apply(this, apply);
}
public Higher1 fold(Applicative applicative) {
return foldMap(FunctionK.identity(), applicative);
}
public FreeAp compile(FunctionK transformer) {
return foldMap(functionKF(transformer), applicativeF()).fix1(FreeAp::narrowK);
}
public FreeAp flatCompile(
FunctionK> functionK, Applicative> applicative) {
return foldMap(functionK, applicative).fix1(FreeAp::narrowK);
}
public M analyze(FunctionK> functionK, Applicative> applicative) {
return foldMap(functionK, applicative).fix1(Const::narrowK).get();
}
public Free monad() {
return foldMap(Free.functionKF(FunctionK.identity()), Free.monadF()).fix1(Free::narrowK);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public Higher1 foldMap(FunctionK functionK, Applicative applicative) {
Deque argsF = new LinkedList<>(singletonList(this));
Deque fns = new LinkedList<>();
while (true) {
FreeAp argF = argsF.pollFirst();
if (argF instanceof Apply) {
int lengthInitial = argsF.size();
do {
Apply ap = (Apply) argF;
argsF.addFirst(ap.value);
argF = ap.apply;
} while (argF instanceof Apply);
int argc = argsF.size() - lengthInitial;
fns.addFirst(new CurriedFunction(foldArg(argF, functionK, applicative), argc));
} else {
Higher1 argT = foldArg(argF, functionK, applicative);
if (!fns.isEmpty()) {
CurriedFunction function = fns.pollFirst();
Higher1 res = applicative.ap(argT, function.value);
if (function.remaining > 1) {
fns.addFirst(new CurriedFunction(res, function.remaining - 1));
} else {
if (fns.size() > 0) {
do {
function = fns.pollFirst();
res = applicative.ap(res, function.value);
if (function.remaining > 1) {
fns.addFirst(new CurriedFunction(res, function.remaining - 1));
}
} while (function.remaining == 1 && fns.size() > 0);
}
if (fns.size() == 0) {
return (Higher1) res;
}
}
} else {
return (Higher1) argT;
}
}
}
}
public static FreeAp pure(T value) {
return new FreeAp.Pure<>(value);
}
public static FreeAp lift(Higher1 value) {
return new FreeAp.Lift<>(value);
}
public static FreeAp apply(FreeAp value, FreeAp> mapper) {
return new FreeAp.Apply<>(value, mapper);
}
public static FunctionK> functionKF(FunctionK functionK) {
return new FunctionK>() {
@Override
public Higher2 apply(Higher1 from) {
return lift(functionK.apply(from)).kind2();
}
};
}
public static Applicative> applicativeF() {
return FreeApplicative.instance();
}
private static Higher1 foldArg(
FreeAp argF, FunctionK transformation, Applicative applicative) {
if (argF instanceof Pure) {
return applicative.pure(((Pure) argF).value);
}
if (argF instanceof Lift) {
return transformation.apply(((Lift) argF).value);
}
throw new IllegalStateException();
}
private static final class Pure extends FreeAp {
private final A value;
private Pure(A value) {
this.value = requireNonNull(value);
}
@Override
public FreeAp map(Function1 mapper) {
return pure(mapper.apply(value));
}
@Override
public String toString() {
return "Pure(" + value + ')';
}
}
private static final class Lift extends FreeAp {
private final Higher1 value;
private Lift(Higher1 value) {
this.value = requireNonNull(value);
}
@Override
public FreeAp map(Function1 mapper) {
return apply(this, pure(mapper));
}
@Override
public String toString() {
return "Lift(" + value + ')';
}
}
private static final class Apply extends FreeAp {
private final FreeAp value;
private final FreeAp> apply;
private Apply(FreeAp value, FreeAp> apply) {
this.value = requireNonNull(value);
this.apply = requireNonNull(apply);
}
@Override
public FreeAp map(Function1 mapper) {
return apply(this, pure(mapper));
}
@Override
public String toString() {
return "Apply(" + value + ", ...)";
}
}
private static final class CurriedFunction {
private final Higher1> value;
private final int remaining;
CurriedFunction(Higher1> value, int remaining) {
this.value = requireNonNull(value);
this.remaining = remaining;
}
}
}
@Instance
interface FreeApplicative extends Applicative> {
@Override
default Higher2 pure(T value) {
return FreeAp.pure(value).kind2();
}
@Override
default Higher2 ap(
Higher1, T> value, Higher1, Function1> apply) {
FreeAp freeAp = value.fix1(FreeAp::narrowK);
FreeAp> apply1 = apply.fix1(FreeAp::narrowK);
return FreeAp.apply(freeAp, apply1).kind2();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy