currentAction) {
+ Objects.requireNonNull(currentAction);
+ this.currentAction = currentAction;
+ evaluator = interpreter;
+ }
+
+ /**
+ * Evaluate the next iteration of the action that this Stepper was constructed with.
+ * Evaluates all actions that are currently visible with {@link Stepper#peek()}.
+ *
+ *
+ * Note that:
+ *
+ * {@code
+ *
+ * // a equals b
+ * List> a = stepper.next();
+ * List> b = stepper.peek();
+ * }
+ *
+ * @return A list of all actions that will be evaluated on the next step.
+ * @throws DoneException if there is nothing more to evaluate.
+ */
+ public List> next() {
+ if (currentAction instanceof Done) {
+ throw new DoneException();
+ } else {
+ Try>> stepped = step(currentAction);
+
+ if (stepped.isSuccess()) {
+ Either> result = stepped.toEither().right().get();
+ if (result.isLeft()) {
+ currentAction = new Done<>();
+ } else {
+ currentAction = result.right().get();
+ }
+ } else {
+ return sneakyThrow(stepped.toEither().left().get());
+ }
+ return peek();
+ }
+ }
+
+ /**
+ * Continue normal evaluation without pausing.
+ * This is equivalent to calling {@link Interpreter#unsafeEvaluate(Action)}
+ * with the current state of the Stepper.
+ * @return The result of running the entire action.
+ * @throws Exception when evaluation fails.
+ */
+ public A runRemainingSteps() throws Exception {
+ return evaluator.unsafeEvaluate(currentAction);
+ }
+
+ /**
+ * Returns all actions that would be evaluated in the next iteration.
+ * Note that not all effects in the action may be visible since sequential
+ * computations like {@code x.then(y)} only allow you to see {@code y} after
+ * {@code x} was evaluated.
+ *
+ *
+ * Use {@link Stepper#next()} to advance the computation.
+ * @return The list of all effects that are visible for the current action.
+ */
+ public List> peek() {
+ return peek(currentAction);
+ }
+
+ protected List> peek(Action> action) {
+ if (action instanceof Done || action instanceof Return) {
+ return List.empty();
+ } else if (action instanceof Suspend) {
+ return peek(((Suspend) action).action);
+ } else if (action instanceof Apply) {
+ return peek(((Apply) action).f).appendAll(peek(((Apply) action).action));
+ } else {
+ return List.of(action);
+ }
+ }
+
+ protected Try>> step(Action action) {
+ if (action instanceof Suspend) {
+ Suspend suspendedAction = (Suspend) action;
+
+ return step(suspendedAction.action).map(result -> {
+ if (result.isLeft()) {
+ return Either.right(suspendedAction.f.apply(result.left().get()));
+ } else {
+ return Either.right(new Suspend<>(suspendedAction.f, result.right().get()));
+ }
+ });
+ } else if (action instanceof Apply) {
+ Apply applyAction = (Apply) action;
+
+ return step(applyAction.action).flatMap(resultOrAction -> step(applyAction.f).map(funOrAction -> {
+ if (resultOrAction.isLeft()) {
+ if (funOrAction.isLeft()) {
+ return Either.left(funOrAction.left().get().apply(resultOrAction.left().get()));
+ } else {
+ return Either.right(new Apply<>(funOrAction.right().get(), pure(resultOrAction.left().get())));
+ }
+ } else {
+ if (funOrAction.isLeft()) {
+ return Either.right(new Apply<>(pure(funOrAction.left().get()), resultOrAction.right().get()));
+ } else {
+ return Either.right(new Apply<>(funOrAction.right().get(), resultOrAction.right().get()));
+ }
+ }
+ }));
+ } else {
+ return evaluator.run(action).map(Either::left);
+ }
+ }
+
+ public static class DoneException extends RuntimeException { }
+ }
+}
diff --git a/src/main/java/previewcode/backend/services/actiondsl/WithSyntax.java b/src/main/java/previewcode/backend/services/actiondsl/WithSyntax.java
new file mode 100644
index 0000000..2a9a0e2
--- /dev/null
+++ b/src/main/java/previewcode/backend/services/actiondsl/WithSyntax.java
@@ -0,0 +1,153 @@
+package previewcode.backend.services.actiondsl;
+
+import io.vavr.*;
+import previewcode.backend.services.actiondsl.ActionDSL.Action;
+
+import java.util.function.Function;
+
+public class WithSyntax {
+
+ public static class W1 {
+ private final Action a;
+
+ public W1(Action a) {
+ this.a = a;
+ }
+
+ public W2 and(Action b) {
+ return new W2<>(a,b);
+ }
+ }
+
+
+ public static class W2 {
+ private final Action a;
+ private final Action b;
+
+ public W2(Action a, Action b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public W3 and(Action