Metaprogramming
Summarized using AI

Ruby Internals

by Patrick Farley

In this video, Patrick Farley delivers a detailed presentation on Ruby internals during the MountainWest RubyConf 2008, primarily focusing on the MRI (Matz's Ruby Interpreter) while also mentioning JRuby and Rubinius. Farley aims to emphasize the importance of understanding Ruby internals, particularly for developers using meta-programming techniques in their applications.

Key points discussed in the presentation include:

  • Understanding Ruby Internals: Farley stresses that while exploring Ruby internals may seem daunting, it provides valuable insights into how Ruby functions. He discusses the potential pitfalls developers may encounter if they do not grasp these concepts fully.
  • Module Behavior & Method Sharing: He illustrates the unexpected behavior of modules in Ruby, particularly the limitations of including module methods as class methods, hinting at deeper implications of method dispatch in Ruby.
  • Meta-Programming Techniques: Farley highlights the prevalent use of meta-programming in Ruby, suggesting that many developers rely on these advanced features without understanding why they work, which can lead to debugging challenges.
  • Method Dispatch: The core of Ruby's behavior lies in method dispatch, where method calls are resolved based on object classes and inheritance hierarchies. He explains how method tables and class pointers affect method resolution in Ruby.
  • Examples and Humor: Throughout the talk, he integrates examples, such as the behavior of methods defined in classes and how instance-specific behaviors can be added—a Ninja class example underscores this point.
  • Books for Further Learning: Farley recommends resources such as the 'Ruby Hacking Guide' and notes the necessity of understanding Ruby's underlying mechanics to avoid programming by coincidence.
  • Conclusion: He concludes by emphasizing that a deeper understanding of Ruby's method dispatch and object behaviors is vital, particularly in complex applications and when utilizing Ruby's dynamic capabilities.

Farley's session underscores not just the complexities of Ruby's behavior but also the responsibility developers have to understand the tools they use, promoting better programming practices within the Ruby community.

00:00:17.440 Hello, everyone. I'm Patrick Farley, a developer with ThoughtWorks, and today I'm going to discuss Ruby internals.
00:00:22.720 To clarify what I mean by Ruby internals,
00:00:28.160 I have some JRuby slides and some Rubinius slides, but mostly, I will focus on MRI stuff.
00:00:34.719 Most of you are likely using MRI, which is Matt's Ruby interpreter, especially if you're on a MacBook Pro.
00:00:41.280 Now, I think it's fair to ask why you should care about Ruby internals.
00:00:47.680 Digging into this topic takes some effort, so what value does it really provide?
00:00:54.559 To illustrate this, I'll look at some Ruby code.
00:00:59.920 I'll focus on the weirder side of Ruby, but it's still pretty standard stuff.
00:01:06.080 For instance, I am defining a module here and creating a method, self.bar, within the module Foo.
00:01:11.680 Anyone who has worked with Ruby knows that there’s trouble ahead.
00:01:17.600 If I’m using modules purely for namespace purposes, like creating a function library called Foo, I should be okay.
00:01:23.520 However, if I intend to use modules for code sharing, meaning multiple inheritance,
00:01:29.280 that’s where I’ll run into trouble.
00:01:35.040 Because anyone familiar with Ruby realizes that when I include this module in a new class, let's say Baz,
00:01:42.079 I won’t get bar as a class method.
00:01:48.079 We learn this early on when we start with Ruby: you can’t bring class methods along when you include a module.
00:01:53.360 It’s peculiar to think about why that is.
00:01:59.520 Similarly, in this example, I'm defining a basic module called Laughable.
00:02:04.560 It has a method, laugh, and this should seem familiar to many people.
00:02:11.599 I extend the class Clown with Laughable, which gives me a class method called laugh.
00:02:16.800 Interestingly, if I create a client object and then extend the Clown object with Laughable,
00:02:22.000 I get an instance method on Clown.
00:02:28.080 Again, this might seem straightforward, but it’s a bit unusual.
00:02:37.280 Ruby provides both length and size methods on arrays, yet it appears to overload extends based on context.
00:02:44.480 There are various syntaxes to define a class method.
00:02:50.640 Many know that when you open up a singleton class and define a method, that magically becomes a class method.
00:02:58.400 The question remains: why is that the case?
00:03:05.440 Let’s look at a specific behavior in Rails.
00:03:11.120 When you call id on nil, it returns four.
00:03:18.560 By default, Ruby was returning four.
00:03:25.040 Rails assumes that most likely you didn’t intend to introduce the number four into your codebase this way.
00:03:33.440 You probably don’t want to do like three over id of nil.
00:03:39.440 So, Rails warns you: it throws an error and says, "By the way, you probably don’t want to do that."
00:03:46.239 It's surprising how this works.
00:03:51.440 By a show of hands, how many of you have read the Pragmatic Programmer?
00:03:58.400 The Ruby community is fantastic.
00:04:03.760 I acknowledge that I'm preaching to the choir a bit here.
00:04:11.280 In the Pragmatic Programmer, there’s a section that discusses programming by coincidence, featuring a programmer named Fred.
00:04:18.400 Fred writes code that works, then writes a bit more code that works, and eventually, his code stops functioning.
00:04:23.840 He struggles to figure out why and faces challenges in debugging.
00:04:28.880 The insight from that practice is that Fred doesn't actually know why his code worked in the first place.
00:04:34.720 My viewpoint is that a significant amount of this occurs in the Rails community.
00:04:40.400 Not just in Rails but in the broader Ruby community as well.
00:04:46.240 When diving into more advanced features of the language, many developers are using meta-programming techniques by example but lack the understanding of why those techniques work.
00:04:52.000 This is a craftsmanship issue.
00:04:57.440 It's our responsibility to thoroughly understand the tools we utilize, rather than waiting until we're in a panic like Fred.
00:05:02.720 We've all encountered moments with meta-programming when things don’t work, leading us to believe that meta-programming might be bad.
00:05:09.919 The intent isn't to discourage using meta-programming, but it is an essential tool to have in our tool belt.
00:05:17.919 If you're developing Ruby-based Domain Specific Languages or more intricate applications, understanding why meta-programming works is crucial.
00:05:25.039 Much of what you encounter concerning modules, mixins, metaclasses, and singleton classes is fundamentally about method dispatch.
00:05:34.720 Matt had many options for implementing specific behavior or mixins;
00:05:40.400 however, he made the elegant decision that method dispatch would always function the same way in MRI with no exceptions.
00:05:46.240 This decision elegantly constrains the implementation options for instance-specific behavior, class methods, modules, and mixins.
00:05:52.000 If you grasp how method dispatch operates in Ruby, it becomes much easier to learn how all these other aspects work.
00:05:59.360 Remember, method dispatch always functions the same way.
00:06:04.960 Regarding book recommendations, I recommend the Ruby Hacking Guide by Minero Aoki.
00:06:11.520 Although it's written in Japanese, there are some partial translations available online.
00:06:17.440 It covers much of the material I’ll discuss today.
00:06:23.760 Although somewhat dated, it still contains valuable insights.
00:06:29.360 I encourage you to Google for partial translations, and if you know Japanese, consider contributing to the translation effort.
00:06:35.760 I'd also like to mention another book that has received positive feedback.
00:06:40.800 Now, let's delve into method dispatch.
00:06:47.120 I have a Ninja class featuring a method called b_awesome, which returns the symbol guitar_solo.
00:06:53.840 I know this sounds silly; you may wonder why guitar_solo is a symbol.
00:06:58.960 I've created an instance of Ninja named Bob and I call the b_awesome method.
00:07:05.760 The question is, how does the b_awesome method get connected to the method information found?
00:07:11.760 Interestingly, I made a point to add props to my slides before the sombrero concept appeared.
00:07:18.000 The sombrero concept was a humorous notion, but it was difficult to visualize.
00:07:24.960 Unfortunately, my efforts to create a visual representation of that idea fell short.
00:07:31.840 I tried my best to insert an image but faced issues;
00:07:37.840 needless to say, I gave up on that.
00:07:42.240 Now, the following is a c-struct r object that represents Ruby objects.
00:07:48.000 In Ruby, everything is an object, and I'm referring to standard objects now.
00:07:54.080 Here's what it looks like without any reduction: a couple of data elements.
00:08:00.640 The first is this basic struct, which we'll describe, and the next is this st_table iv table.
00:08:06.160 So, while it seems complex, let’s break it down.
00:08:12.160 Heroes fans, ever notice how certain characters reflect specific traits?
00:08:18.000 There are analogies in the relationship between C and Ruby programmers.
00:08:25.200 They often express confusion when they initially experience C programming concepts.
00:08:32.560 Given the second part, the st_table instance variable table or sc_table acts as a hash implementation.
00:08:39.360 The iv stands for instance variables, so Ruby knows an object’s instance variables, stored in a hash.
00:08:45.120 The other aspect Ruby recognizes is tied to this r_basic.
00:08:51.680 There are only three things everyone knows about an object: its instance variables, its class, and its properties collection.
00:08:58.080 Importantly, behavioral aspects are missing; Ruby functions differently from prototype-based languages.
00:09:03.760 This is a useful perspective when thinking about metaclasses, singleton classes, and so on.
00:09:09.760 Understanding that Ruby’s design encourages storing behavior at a class level is key.
00:09:15.600 When discussing Ninja and b_awesome, this behavior must reside somewhere.
00:09:22.640 Let’s take a look at the struct that represents a class.
00:09:31.360 It's interesting to consider classes as objects in MRI.
00:09:37.120 From a usage perspective, they are, but from an implementation level, it's a different story.
00:09:44.080 Classes—on the surface—stem from a unique struct, as you're probably seeing.
00:09:50.560 The first two elements you'll see are familiar and an instance variable table.
00:09:57.480 But two important distinctions exist: a method table containing behavior and a superclass pointer.
00:10:04.080 Trajectory of method dispatch is crucial.
00:10:09.680 When sending a message to an object, the interpreter dereferences the class pointer.
00:10:16.000 It looks for the method in the method table hash.
00:10:23.200 If not found, it processes the super pointer iteratively.
00:10:28.240 The interpreter keeps referencing until ultimately hitting null and declaring method missing.
00:10:35.680 Visually, this works like this.
00:10:41.600 The class pointer dereferences and searches for the method within that hierarchy.
00:10:48.720 After understanding method dispatch, we can discuss singleton classes and instance-specific behavior.
00:10:56.080 Consider a new Ninja named Joseph.
00:11:03.760 But here’s the twist: Joseph is a pirate hiding as a ninja.
00:11:10.240 He has a specialized method called speak.
00:11:16.640 If you're familiar with Ruby, you might recognize this as defining joseph.speak.
00:11:22.560 This is one way to add instance-specific behavior.
00:11:28.640 In this case, I’ve added the method directly to Joseph.
00:11:34.560 When he speaks, he says 'arr', while Bob, being a ninja, can’t articulate anything.”},{
Explore all talks recorded at MountainWest RubyConf 2008
+28