title: Using lambda to provide stack-based context (eg. path for file manipulation)
body: <p>I've though of a subtle way (which might have already be implemented and discussed) to do the equivalent of this in Groovy (taken from the <a href="https://stackoverflow.com/questions/25565020/is-java-lambda-expression-is-similar-logic-of-groovy-closure#comment39927502_25567047">Groovy documentation here</a>) and I am trying to improve it, if that is possible:</p>
  
  <pre><code>ant.sequential {
      echo("inside sequential")
      def myDir = "target/AntTest/"
      mkdir(dir: myDir)
      copy(todir: myDir) {
          fileset(dir: "src/test") {
              include(name: "**/*.groovy")
          }
      }
      echo("done")
  }
  </code></pre>
  
  <p>This example is fine, but I'm not doing an Ant based program: I want to do some fluent API to manipulate files and directories (let's call it <em>FileManipulator</em>, and I'm bad with names :)). </p>
  
  <p>Basically this would be that:</p>
  
  <pre><code>new DefaultFileManipulator(Paths.get("root")).with(() -&gt; {
    newFile("file1");          // create file      root/file1
    newFile("file2");          // create file      root/file2
    cd("directory1", () -&gt; {   // create directory root/directory1
      newFile("file1");        // create file      root/directory1/file1
      cd("directory1", () -&gt; { // create direcotry root/directory1/directory1
        newFile("file1");      // create file      root/directory1/directory1/file1
      });
      newFile("file2");        // create file      root/directory1/file2
    });
  }); 
  </code></pre>
  
  <p>With the following interface:</p>
  
  <pre><code>interface FileManipulator {
    FileManipulator with(LambdaFileManipulator m);
    FileManipulator cd(String path, LambdaFileManipulator m);
    Path newFile(String path) throws IOException;
  }
  
  @FunctionalInterface
  interface LambdaFileManipulator extends FileManipulator {
    void execute() throws IOException;
    default FileManipulator with(LambdaFileManipulator m) {
      return FileManipulatorStack.manipulatorFor(this).with(m);
    }
    // for each non default method of FileManipulator, the same call to FileManipulatorStack.manipulatorFor(this).
  }
  </code></pre>
  
  <p>The <code>DefaultFileManipulator</code> simply implements <code>FileManipulator</code> and use the <code>AbstractFileManipulator</code> defined below. </p>
  
  <p>Because I want the path to be relative, I need somehow to keep the lambda context (I don't want to mess the current working directory): I do that using a stack which is manipulated like this:</p>
  
  <pre><code>// visibility package, because that's technical stuff!
  class FileManipulatorStack {
    private static final Map&lt;LambdaFileManipulator , ArrayDeque&lt;LambdaFileManipulatorDelegator&gt;&gt; stacks = new ConcurrentHashMap&lt;&gt;();
  
    static LambdaFileManipulatorDelegator manipulatorFor(final LambdaFileManipulator delegatee) {
      final ArrayDeque&lt;LambdaFileManipulatorDelegator&gt; stack = stacks.get(delegatee);
      if (null == stack) {
        throw new IllegalStateException("state is empty for [" + delegatee + "]");
      }
      return stack.getLast();
    }
  
    static void delegate(final Path scopedPath, final LambdaFileManipulator delegatee) {
      final LambdaFileManipulatorDelegator handler = new LambdaFileManipulatorDelegator(scopedPath);
      final ArrayDeque&lt;LambdaFileManipulatorDelegator&gt; stack = stacks.computeIfAbsent(delegatee, key -&gt; new ArrayDeque&lt;&gt;());
      stack.addLast(handler);
      try {
        delegatee.execute();
      } catch (final Exception e) {
        throw new DelegatedFileCreatorHandlerUndeclaredException(e);
      } finally {
        final LambdaFileManipulatorDelegator ss = stack.removeLast();
        if (ss != handler) {
          throw new IllegalStateException("invalid stack");
        }
        if (stack.isEmpty()) {
          stacks.remove(delegatee);
        }
      }
    }
  
    static class LambdaFileManipulatorDelegator extends AbstractFileManipulator {
       ... constructor ... 
    }
  }
  
  abstract class AbstractFileManipulator implements FileManipulator {
    private final Path root;
    public AbstractFileManipulator(Path root) {
      this.root = requireNonNull(root, "root");
    }
  
    public final FileManipulator with(LambdaFileManipulator m) {
      FileManipulatorStack.delegate(root, m);
      return this;
    }
  
    public final FileManipulator cd(String path, LambdaFileManipulator m) {
      FileManipulatorStack.delegate(root.resolve(path), m);
      return this;  
    }
  
    public final Path newFile(String path) {
      Path p = root.resolve(path);
      Files.createFile(p);
      return p;
    }
  }
  </code></pre>
  
  <p>My question might not be "good" in regard to Stackoverflow rules, but here is: </p>
  
  <p><strong>How can I improve this without adding the <code>FileManipulator</code> as parameter to lambda (I would have use <code>Consumer</code> for that instead)?</strong></p>
  
  <p>Is there problem by using the lambda as key to a map? (in <a href="https://stackoverflow.com/questions/25565020/is-java-lambda-expression-is-similar-logic-of-groovy-closure#comment39927502_25567047">this comment</a>, it says the lambda will be dynamically converted to an instance of <code>LambdaFileManipulator</code>, then I perhaps don't need a map of stack).</p>
  
  <p>Did I miss some feature of Java 8 that would allow me to make a lambda an implementation method of some class? </p>
  
  <p><strong>Edit:</strong> I answered my own question ... and this can't work because the lambda does not/never know it is an implementation of <code>FileManipulator</code>. So, it can not call those methods. It might work using static methods (and some context), but I think it is worse than having a parameter.</p>