Mirah Office Hours: Closures in Closures

Way back in November, four months ago, I embarked on an adventure. I wanted to render views in Shatner in a friendly way. Unfortunately, at the time Mirah didn’t support defining closures inside other closures(#155). This is a big problem for all sorts of interesting use cases.

In my particular use case, I was trying to use a closure to get around another issue with Mirah’s edb plugin, where it didn’t like being passed unquotes (#152). My thought was that I could use edb to generate a render method on a closure class that would represent the view. That way, the view would have access to the environment inside the get block through the binding object that Mirah generates as part of how it handles closure creation.

The problem was that blocks that represent closures didn’t have the right kind of back reference to the ClosureDefinition that was added to them by the transformer. When the transformer generated the ClosureDefinition, it didn’t tell the block about it. Because the block didn’t know, it couldn’t tell closures inside it what scope they were in.

A ClosureDefinition node is an AST node representing the class definition of a closure. The code generator uses these nodes, along with their attached blocks to make those Class$1234.class files you see when you have closures in Java.

An Example


def foo r:Runnable
  #...
end

foo do          # Block 1.
  foo do        # Block 2.
    puts 1
  end
end  

During the transform phase, the transformer adds a ClosureDefinition node for Block 1 that uses the outer class for it’s scope. It creates a constructor for the new ClosureDefinition. The constructor takes a binding that it will share with the enclosing scope.

Then the transformer looks at the body of Block 1, to use it to create method definitions on the ClosureDefinition for the abstract methods of the type that the Block is implementing. For Block 1, that’s run.

It gets to Block 2, and tries to create a ClosureDefinition for it. But that fails, because Block 1 doesn’t know its own type. It doesn’t have a reference back to the its ClosureDefinition.

The Fix

The error you would get with this was "undefined method `defining_class' for #<Mirah::AST::Block:0x2f267610>". This was because most scoped body types had a defining_class method that pointed at the AST node representing the class they were defined on. Blocks didn’t. The way I fixed it was by adding a defining_class method on Block and initializing the instance variable it pointed to with the ClosureDefinition created during the transform step.

Plans

This weekend I’m hoping to either get back to working on Shatner, or to start working on a REPL for Mirah. I’ve already learned a little bit about how Mirah’s binding generation works–to build a REPL, I’d need to master it.

New AST

Also, a few weeks ago I finally merged master into the newast branch. The newast branch is where ribrdb has been working on making Mirah more self-hosted, which should make it much faster, as well as providing a good place to work out some of the edge cases in the language. Merging master is important because otherwise the more experimental branch will diverge too much & become harder to merge back in later.

W00t!

Comments are closed.