A recurring practice of mine these days is to take a feature of a language and see if I can replicate its functionality or likeness in Ruby. Sometimes this is because the feature is one I wish Ruby supported, but in most cases its simply a fun exercise to see if Ruby is capable of doing such a thing and seeing what the design implications would have on my Ruby code.
Lately I’ve been switching frequently between Clojure and Ruby. This has me curious about how close I can get Ruby to look like Clojure in size and style. Recently, what has bothered me most has been how you define a namespace in Ruby by nested modules. Compared to other languages like Clojure, Java, or Haxe, Ruby can seem rather verbose. For example, here’s Clojure:
This is much more terse than Ruby’s deeply nested namespaces:
Even Ruby’s inline syntax, which you may not want to use, seems comparatively clunky:
Lets fix that. What I want is a syntax that lets me state clearly the namespacing in which my code should be found, without needing all that nesting. After a little bit of trial and error, I came up with this syntax. The original gist of this can be found here.
Simple, short, and clean. Now lets walk through the code that does the magic.
The ns method ends up being a wrapper of the method that does all the work and a bridge to the DSL we are providing.
I have an optional parameter specifying what the delimiter is for the namespace. This both provides flexibility in the input we accept and abstracts away having a magic string in the method body.
I am taking Ruby’s implicit block and converting to a proc argument for our method. The reasoning behind this is syntax. If the proc were a normal argument, it would make the method call awkward.
Now for the method that does all the work.
It's dense, but readable. If it is not immediately obvious, this is a recursive method that will call itself for every namespace in the list.
The method begins with the end, the leaf node, the base case, what have you. If there are no more module_names
left, it evaluates the block of code provided in the context of the current module. If there are more module_names
, it continues creating nested modules.
The helper method find_or_create_const
that takes the current module and a ‘constantized’ string version of the first module_name
. It returns that constant if it has been already defined within the current module scope. Otherwise, it creates a new empty module with the given name.
A tap block allows us to work with the constant returned from find_or_create_const
without the need for an intermediate variable. This is one of the few times when I feel the call to tap provides more intent and adds more clarity to the ‘story the method is telling’.
Moving into the block:
This name of this method is longer than all the code within it. I’m sure there is a better name out there, but I'm also certain it'll be the same length. The current name explains what it is doing, but not how it's doing it or what doing that means. The module extend self
call is a way to turn every instance method in a module into a singleton method. This eliminates the need to repeat the def self.foo
pattern in every method.
Ending the method is a call to module_exec
in the context of the new module and then a recursive call to itself after dropping the first name from the module_names
list.
The one limitation I ran into with this was that you cannot nest any constants inside your namespace unless you call const_set
. Due to the way Ruby parses blocks and evaluates constants they will always end up at the top level namespace. I've packed the finished code into a gem if anyone is interested in trying it out.
I was happy to find a syntax that both cleans up the Ruby nested module boilerplate while still feeling like Ruby code in the end. A great approach for learning ways to tackle new or existing problems is to adopt the perspective of a different language or paradigm.