package xapi.fu; import java.util.function.BiFunction; /** * @author James X. Nelson (james@wetheinter.net) * Created on 07/11/15. */ @SuppressWarnings("unchecked") // yes, this api will let you do terrible things. Don't do terrible things. public interface In2Out1<I1, I2, O> extends Rethrowable, Lambda { O io(I1 in1, I2 in2); default int accept(int position, In1<O> callback, Object... values) { final I1 i1 = (I1) values[position++]; final I2 i2 = (I2) values[position++]; final O out = io(i1, i2); callback.in(out); return position; } default BiFunction<I1, I2, O> toFunction() { return this::io; } static <I1, I2, O> In2Out1<I1, I2, O> of(In2Out1<I1, I2, O> lambda) { return lambda; } static <I1, I2, O> In2Out1<I1, I2, O> of(In2<I1, I2> in, Out1<O> out) { return (i1, i2)-> { in.in(i1, i2); return out.out1(); }; } /** * This method just exists to give you somewhere to create a lambda that will rethrow exceptions. */ static <I1, I2, O> In2Out1<I1, I2, O> unsafe(In2Out1Unsafe<I1, I2, O> of) { return of; } default In1Out1<I2, O> supply1(I1 in1) { return in2->io(in1, in2); } default In1Out1<I2, O> supply1Deferred(Out1<I1> in1) { return in2->io(in1.out1(), in2); } default In1Out1<I2, O> supply1Immediate(Out1<I1> in1) { final I1 i = in1.out1(); return in2->io(i, in2); } default In1Out1<I1, O> supply2Deferred(Out1<I2> in2) { return in1->io(in1, in2.out1()); } default In1Out1<I1, O> supply2Immediate(Out1<I2> in2) { final I2 i = in2.out1(); return in1->io(in1, i); } default In1Out1<I1, O> supply2(I2 in2) { return in1->io(in1, in2); } default Out1<O> supply(I1 in1, I2 in2) { return supply1(in1).supply(in2); } static <I1, I2, O> In1Out1<I2,O> with1(In2Out1<I1, I2, O> io, I1 in1) { return in2 -> io.io(in1, in2); } static <I1, I2, O> In1Out1<I1,O> with2(In2Out1<I1, I2, O> io, I2 in2) { return in1 -> io.io(in1, in2); } /** * This hideous looking method enables * you to perform inline compute operations, * where you can retrieve a value from an object, * transform that object into a new value, * store that back into the original map, * then return the new value. * * While terrible to read, this method can be used as follows: * * <pre> * Map<String, Integer> map = new HashMap<>(); * map.put("key", 0); * In3Out1.transformCompute(map, Map::get, Map::set) * .io(map, "key", (k, v)->v++); * assert map.get("key") == 2; * * When using a Map class, you can use X_Collect.computeMapTransform, * which will supply the Map::get and Map::put method references for you. * * </pre> */ static <Obj, Key, Val> In2Out1<Key , In2Out1<Key, Val, Val>,Val> computeKeyValueTransform(Obj obj, In2Out1<Obj, Key, Val> getter, In3<Obj, Key, Val> setter) { return (key, t) -> { Val v = getter.io(obj, key); Val c = t.io(key, v); setter.in(obj, key, c); return c; }; } interface In2Out1Unsafe <I1, I2, O> extends In2Out1<I1, I2, O> { O ioUnsafe(I1 i1, I2 i2) throws Throwable; default O io(I1 i1, I2 i2) { try { return ioUnsafe(i1, i2); } catch (Throwable e) { throw rethrow(e); } } } }