As I wrote in my previous post I would write on how Moby maroon works internally. This is definitely not a post explaining DCI. As a matter of fact it might confuse you and potentially impair your understanding of DCI so tread carefully if you are new to DCI. The target audience is Rubyists that want to know how Moby maroon is capable of getting performance on par with regular method invocation and/or those that might want to contribute to Moby maroon and thereby indirectly to Marvin and DCI as well,
The context defined in the previous post is rewritten to an ordinary ruby class the source code of this class is returned as one of two objects from the define method. The first object being returned is the newly created class it self. The second object is the actual source of this class. In the case of the hello world example the source looks like this
class Greeter def greet p("#{greeting} #{self_who_say}!") end @who @greeting private def who;@who end def greeting;@greeting end def self_who_say who end end
As you can see there’s really nothing magically going on. So what did actually happen? Well firstly each role is rewritten to a definition of an instance variable and a getter. So for the who role there’s an instance variable ‘@who’ and a method ‘who’ and similar for the ‘greeting’ role. But there’s no role methods any more, where did the role method go? The last method in the source
def self_who_say
who
end
is where the role method went. The name might hint at this. Role methods are rewritten into instance methods of the context. And this is where you might be confused if you are new to DCI, so it’s worth stressing that this rewrite violates the mental model of DCI. Paradoxically DCI is very much about expressing the mental models and not violating them. A role method should be seen as a part of the role player and never be considered a part of the context.
it would have been safe to rewrite this to
def self_who_say
@who
end
So why isn’t it done? The reason to this is that there’s two rewriting phases in Moby maroon. The first rewrites the use of self to the corresponding role getter and the second rewriting phase depends on the role getter. When a role getter is encoutered the rewriter will check to see it’s the instance expression of a role method invocation. If it is it will rewrite that invocation. E.g if we changed the who role to
role :who do say do self end talk do self.say end end Then the first phase would rewrite talk to
talk do
who.say
end
And the second phase would the see this as a role method invocation and rewrite it to
def self_who_talk self_who_say end
Recollecting from the previous post that if you call a method directly on the instance variable (in this case ‘@who’) it will always be an instance method and never a role method it should be obvious why the first phase rewrites to the role player getter and not the instance variable. Rewriting self to the instance variable would result in the second phase being unable to identify role method invocations.
Having seen how role method invocations are translated into invocations of instance method on the context obejct it should also be clear that there can be no performance difference between calling a role method or calling a regular instance method after all seen from the view of the interpreter role methods are instance methods.
In the next post we will look at how to bind block variables to roles followed by another post on how the rewriting handles this.
Is there hope for sharing roles between contexts?
Sharing roles between context is by definition not an option in DCI (it’s related to what a role is). It makes for a good subject for a post. So I’ll take that up in a later post
[…] How Moby works […]