In Defense of Alias

Comments

As some of you already know, I've recently started a new job as Director of Engineering at nVisium.

One of the first few things nVisium requested of me was to develop a coding style guide, so our code would read more consistently. Naturally, I used the community-driven guide maintained by Bozhidar Batsov (author of RuboCop) as a starting point, but ended up making my own tweaks (style is subjective, after all!).

Thanks to the magic of git diff I now have a record of styles I feel have gotten an unnecessarily bad rap, and I want to talk about one of them today: I prefer alias over alias_method.

Arguments Against alias

The arguments I've seen against using alias instead of alias_method are:

  1. It's a keyword, not a method, so it doesn't need a comma between its arguments, which can be barewords. This can throw some people off.
  2. alias_method is a method on Module, so it can be redefined if you like.
  3. alias is lexically scoped. This means that self for purposes of the lookup of the method being aliased is whatever self is where it's encountered in the source code, not whatever self is at runtime.

How I Use alias

When aliasing methods, especially in non-library code, I'm doing so at the class level. Something like:

class MyContainer

  # ...

  def store(key, value)
    @values[key] = value
  end
  alias :[]= :store

end

In this context, it absolutely makes sense that alias is a keyword. It's just like def in terms of its results (a method), but differs in the way that it determines what code is executed when the method is dispatched. Note that I use symbols instead of barewords so that my syntax highlighting looks nicer. Symbols totally work. You don't have to use barewords!

As far as the argument that alias_method can be redefined: Don't redefine methods like alias_method, and especially not on core classes like Module. If you really need some kind of alternate alias behavior, define your own in a module, and include or extend it as needed. It's totally cool if you want to use alias_method inside your own method.

The argument that obviously carries the most weight is the one about lexical scope, so let's take a closer look at that one.

The Lexical Argument, In Depth

The argument against lexically-scoped aliases (and in favor of alias_method) is as follows:

Say you have some classes like this:

class Person

  def full_name
    'Bob Bobbenheimer'
  end
  alias :name :full_name

end

class Developer < Person

  def full_name
    "#{super}, Software Developer"
  end

end

If you instantiate Developer, you might think that Developer#name would return "Bob Bobbenheimer, Software Developer", but it won't. This is because the alias defined in Person refers specifically to the full_name method as it existed at the location in the source at which the alias keyword was used.

Now, if it were the case that we could rewrite Person as...

class Person

  def full_name
    'Bob Bobbenheimer'
  end
  alias_method :name, :full_name

end

...and the code would behave as we'd assume, then I would agree: we should always use alias_method. But, of course, that isn't the case. A call to alias_method inside a class body will be executed as the class is loaded, so once again, self refers to the same method that alias would: the full_name method as it was defined in Person.

Hmm, maybe if you are doing some cutesy metaprogramming-type stuff like:

class Person

  def self.inherited(subclass)
    subclass.class_eval { alias_method :name, :full_name }
  end

  def full_name
    'Bob Bobbenheimer'
  end

end

Nope, in that case, the inherited hook gets called before Developer would have defined its own full_name method, so the alias will still point to Person#full_name. The specific case in which the differences would manifest is:

class Person

  def self.add_name_alias
    alias_method :name, :full_name
  end

  def full_name
    'Bob Bobbenheimer'
  end

  add_name_alias

end

class Developer < Person

  def full_name
    "#{super}, Software Developer"
  end

  add_name_alias

end

In this case, using alias in the Person.add_name_alias method would still resolve to Person#full_name, while alias_method will resolve to Developer#full_name, because its notion of self is determined at runtime.

This example is obviously contrived for discussion's sake, but the more generally-applicable thing to note here is that this issue will only bite you if you are aliasing things at runtime. In real-world scenarios, this probably means you are monkey patching something or writing some clever class macros.

On Brain Compatibility

And this is why I default to using alias, and not alias_method. It states something to the reader.

Dear reader of this code, be you future me or someone I haven't met yet, I guarantee to you that the method to which I'm pointing is discernable by simply reading the source code you see here, as humans generally do.
alias

Contrast this with:

Dear reader of this code, be you future me or someone I haven't met yet: I could be pointing at anything. Hope your test suite's great. YOLO!
alias_method

Because here's the thing: when we read code, our brain naturally processes code (and most written communication, for that matter) in lexical scope. We look to the context around our code for clues as to how to interpret it.

There are more Rubyish ways to indicate to the reader that a method should be looked up at runtime. Here's my favorite:

class Person

  def full_name
    'Bob Bobbenheimer'
  end

  def name
    full_name
  end

end

class Developer < Person

  def full_name
    "#{super}, Software Developer"
  end

end

Look at that! No class macros. No aliasing. No metaprogramming. It Just Works™.

You exclaim, "but now you're dispatching twice! Your ROFLscales will suffer!"

Fair enough. I'll cross that bridge when I come to it.

When Should I Resort to alias_method?

So, with that out of the way, I still sometimes use alias_method when I need it. The following legitimate use cases come to mind:

  1. I want to alias to a method being determined by a variable name. Even without scoping issues, alias's support for barewords make using a variable name impossible.
  2. I am intentionally looking up a runtime binding for an alias. This means I am aliasing from within another method. This means I am probably doing some cutesy metaprogramming trick. This means I should consider the life choices that have led me to this point, and determine if there's a clearer way to accomplish what I want to do.

By defaulting to alias and only resorting to alias_method when it's necessary, I'm intentionally creating one of those helpful points for reflection on why I'm doing what I'm doing. Often, a better application design is the result.

comments powered by Disqus