RailsConf 2018

Ruby: A Family History

Ruby: A Family History

by Geoffrey Litt

In the talk titled "Ruby: A Family History," Geoffrey Litt explores the influences of older programming languages—Lisp, Smalltalk, and Perl—on the design of Ruby, one of the most beloved programming languages among developers. The discussion highlights Ruby's unique characteristics, stemming from its rich heritage, and aims to deepen the audience’s understanding of Ruby's foundations, ultimately showcasing how these influences contribute to Ruby's elegance and usability.

Key Points Discussed:
- Why Developers Love Ruby: Geoffrey introspects why Ruby resonates with many, emphasizing the personal and subjective nature of programming language preferences. He argues that understanding Ruby's influences can enhance our use and appreciation of it.
- Influence of Lisp: Ruby inherited many principles from Lisp, introduced by John McCarthy. Key features like dynamic typing, garbage collection, and first-class functions in Ruby reflect Lisp's foundational concepts. Geoffrey also explains the significance of how Lisp enables abstract computation, influencing Ruby’s design philosophy that centers around human thought processes rather than machine constraints.
- The Role of Smalltalk: Geoffrey outlines the impact of Smalltalk as the first object-oriented programming language, highlighting its emphasis on making programming accessible to everyone. He discusses how Ruby’s object-oriented features borrow heavily from Smalltalk, enabling dynamic behavior and messaging passing, an archetype fundamental to Ruby’s functionality.
- Lessons from Perl: Perl is depicted as a pragmatic language focused on efficiency and flexibility, often prioritizing getting the job done. Ruby adopts many of Perl’s syntactical conveniences, such as string interpolation, and the philosophy of allowing multiple ways to achieve a task, thus fostering an empowering programming environment.
- The Balance of Ideas: The crux of Geoffrey’s argument is the balance Ruby strikes between consistent computational models and practical usability. He concludes that Ruby reflects the thoughtful integration of inspirations from its "family" languages, combining complexity with ease of use.

Conclusion and Takeaways:
- Ruby embodies a blend of principles from its precursor languages, emphasizing the importance of adapting ideas for broader applicability in programming.
- Developers should recognize that the design choices in Ruby provide significant insight into its capabilities, inviting them to harness its full potential in application development.
- Understanding the historical context of programming languages like Ruby can inspire thoughtful architecture and design in software engineering and beyond.

00:00:10.880 Hello, everybody. My name is Geoffrey and I have a confession to make: today I love using Ruby. There's just something about it that resonates with me.
00:00:18.840 I wouldn't be surprised if many of you in this room feel the same way. There's something special about this programming language, and I’m really interested in the question of why that is.
00:00:32.009 What is it about Ruby that makes people fall in love with it? To some extent, this is a personal question—it might just be a matter of taste. However, I believe we can utilize tools to systematically analyze this question.
00:00:49.559 The value of this analysis goes beyond intellectual curiosity. As engineers, when we aim to design successful systems, it’s crucial that we examine other successful systems and learn from why they work and why we like them.
00:01:01.050 Whether it’s a programming language, framework, or other products, there’s a lot to be gained from this sort of analysis. Today, we’re going to dig into the question of what makes Ruby what it is.
00:01:26.490 We’ll explore the design of this language that we all love through the lens of comparing Ruby to other programming languages.
00:01:32.490 Yukihiro Matsumoto, or Matz, the creator of Ruby, is an enthusiastic programming language advocate. He loves checking out unique and new programming languages.
00:01:46.979 He believes that by learning other languages, we can broaden our understanding of what’s out there and gain deeper insights into Ruby itself. One way to approach this is to compare Ruby to other modern programming languages like Rust or Python.
00:02:08.420 While there’s value in comparing Ruby to these languages, I think it's more effective to understand a programming language through its roots—its family. To truly appreciate Ruby, we must meet its family and explore the prominent programming languages that contributed to Ruby’s design: Lisp, Smalltalk, and Perl.
00:02:37.680 As we go through these languages today, we’ll provide a brief introduction for those unfamiliar with them, and we’ll also delve deeper. My goal is not just to present these languages in isolation but to examine how they collectively contributed to the cohesive language that Ruby is, and what we can learn from the design decisions that Matz made.
00:02:57.060 Moreover, there are many interesting, forgotten ideas from these languages that didn’t quite make it into Ruby and might have been overlooked today. Therefore, we may find inspiration in these ideas for how to advance the way we program.
00:03:16.079 Let’s get started with Lisp. Can I see a show of hands? Who in here has ever used Lisp before? Great, a good number of you. For those of you who didn’t raise your hands, you might be missing out.
00:03:35.489 Interestingly, Ruby was originally designed with principles from Lisp. According to Matz, he mentioned it could have even been called 'Matz's Lisp.' For those of you who are familiar with Lisp, this might come as a surprise, but I’ll explain what he meant.
00:04:00.500 So, what is Lisp? Back in 1958, a computer scientist named John McCarthy was pondering how to describe computation and algorithms in a new way. He developed a new set of axioms to describe computation that ultimately led to the creation of Lisp, which stands for 'list processing language.'
00:04:14.670 Lisp was underpinned by a simple concept: it utilized data structures known as lists and a few basic operators for manipulating those lists. The profound thing about lists is that from just these primitives, you can describe any arbitrary computation.
00:04:40.210 A classic example of this power is that you can write an interpreter for the Lisp language itself using just a small set of primitives. To appreciate the significance of this achievement in computing history, we can think about programming languages on a spectrum ranging from low-level to high-level.
00:05:11.030 Low-level languages are close to the hardware, while high-level languages are closer to the way humans think abstractly. Prior to Lisp, we were slowly climbing this ladder, employing better ways to express how machines worked, but still operating at a lower level.
00:05:29.810 Lisp came in from a completely different angle. McCarthy was not concerned with how to paper over the workings of computers; he was thinking about describing computation in an abstract manner, detached from the machinery of the computers themselves.
00:05:58.039 This distinctiveness led some to claim that 'Lisp was discovered, not invented.' When considering Ruby, you can see a strong lineage from this idea to this day: we want our systems to be designed around how people think rather than how machines think.
00:06:22.560 The system will work to understand how to interpret your thoughts. It ties back to the concept of conceptual compression that David spoke about in his keynote yesterday. Lisp represents a lot of conceptual compression and clarity, and we see that echoed in Ruby.
00:06:46.430 Since Lisp was the first truly high-level language, it introduced many specific features that Ruby inherited, such as conditionals, dynamic typing, garbage collection, and symbol types.
00:07:06.210 One particularly intriguing aspect to examine is how Ruby incorporates Lisp's approach to functions. In Lisp, functions were treated differently from those in prior languages—they are simply values that can be created and manipulated at runtime.
00:07:23.550 For instance, we can use the 'lambda' keyword in Lisp to define an anonymous function that takes an input and multiplies it. Lisp also introduced the notion of higher-order functions like 'map,' which can accept functions as arguments and perform operations with them.
00:07:50.600 If you’ve ever used 'map' in Ruby, you can appreciate the power of this approach. In Ruby, we can directly translate this code, as we also have a 'lambda' keyword and function types inherited from Lisp.
00:08:14.110 Some of you may be squinting because this isn’t how we typically use 'map' in Ruby. We often use this construct called blocks. We’ve all become accustomed to using blocks, but have you ever thought about what blocks really are and why they exist?
00:08:30.420 So, what are blocks? They’re somewhat peculiar. Unlike everything else in Ruby, blocks are not objects. If you type 'block' into IRB, it's not a standalone entity. Instead, you must define a method that accepts a block as an invisible argument.
00:08:55.290 So what’s the purpose of this? I believe blocks provide a concise syntax for the occasions when you need to pass one anonymous function to another function—just one. It turns out that this situation is remarkably common and very useful.
00:09:19.410 Here we have a series of functional transformations applied to some data in Ruby. Ruby provides a cleaner syntax because we often find ourselves passing just one function into another.
00:09:43.260 If this were in Lisp, the stack might look cluttered with parentheses and 'lambda' keywords everywhere. Ruby’s compact syntax for passing a single anonymous function is much neater, allowing for all sorts of flexibility.
00:10:04.630 This style is commonly seen in Rails, in migrations, rake tasks, and so on. I would argue there is a certain inconsistency in Lisp's approach since you have this one syntactic tool, 'lambdas,' that you can apply anywhere. In Ruby, we have blocks, procs, and lambdas.
00:10:20.090 Initially, it took me some time to grasp the differences among these various constructs. If you ever need to pass two anonymous functions to another function, you can no longer use blocks, switching instead to a different syntax altogether.
00:10:38.030 While this might seem more complicated, these blocks provide significant advantages to the language. I think this teaches us that while consistency is an important design goal, sometimes a little inconsistency to optimize special cases that are heavily leveraged can be beneficial.
00:10:56.140 Now, let’s talk about syntax. Here’s an example of Lisp syntax, which appears quite different from Ruby. This is known as s-expressions. Interestingly, this iconic Lisp syntax was originally not intended to be the final syntax.
00:11:07.600 It was meant to be the internal syntax used by the interpreter, but someone in the research group wrote an interpreter using this syntax directly, leading to its popularity. John McCarthy, the creator, wasn’t a fan of this and preferred m-expressions instead but ended up with s-expressions.
00:11:29.660 This situation is relatable for many in the software engineering field. McCarthy quipped that the project was neither finalized nor explicitly abandoned but merely faded into the indefinite future.
00:11:42.390 Lisp syntax has its benefits; it mirrors the abstract representation of code quite well, allowing for minimal parsing in the language. It enables advanced features like macros to manipulate code quite flexibly.
00:11:56.610 Despite the merits of Lisp's syntax, one might wonder why Ruby didn't adopt it directly. Ruby’s syntax is actually derived from a different language called Fortran.
00:12:12.410 Regardless of whether one prefers Lisp syntax or not—there are endless debates on that topic—there was a clear winner by the time Matz developed Ruby in 1992. Most of the programming languages we use today adhere to the Fortran tree of syntax.
00:12:44.360 Navigating the landscape of programming languages, sometimes adopting trends becomes necessary for maintaining relevance. If Ruby had opted for Lisp syntax, I doubt we’d be here today using it professionally.
00:13:03.620 Now, there’s an intriguing line of inquiry: are there any interesting functionalities in Lisp’s syntax that we might implement in Ruby? Yes, there’s a gem available for Ruby—a parser that can interpret any Ruby code as raw text and parse it into an abstract syntax tree for you.
00:13:19.400 This tree represents data, which you can manipulate and later unparse back into Ruby code. You might think this is a poor idea, and you wouldn’t be wrong, but there are interesting use cases.
00:13:33.540 For example, there's a gem that performs mutation testing by randomly modifying your code to ensure your tests fail, employing this type of parsing under the hood. It’s thought-provoking to consider whether we can adapt some of the capabilities of Lisp macros into Ruby.
00:13:59.710 Undoubtedly, we rarely need to rely on such a technique in Ruby because we have other robust programming tools at our disposal. This brings us to our next language: Smalltalk.
00:14:15.600 Can I see a show of hands? Who here has encountered Smalltalk before? A few hands—fewer than those for Lisp. Smalltalk was the first object-oriented programming language and had a significant impact on Ruby.
00:14:32.420 This groundbreaking language was primarily developed by Alan Kay and his collaborators, Adele Goldberg and Dan Ingalls, at the renowned Xerox PARC lab during the 1970s.
00:14:47.290 Kay’s approach differed from McCarthy's; his focus lay on the human aspects of computing. He envisioned a world where anyone could write programs for their personal use—be it an architect designing software or children creating games.
00:15:02.290 This vision aligns well with the notion of making technology accessible—a theme David touched upon yesterday. The key was to create the right abstractions while compressing a great deal of complexity behind the scenes.
00:15:21.990 In line with this vision, Kay cleverly named the programming language Smalltalk. The focus was on creating a system that would engage children, allowing them to utilize the language effectively.
00:15:37.490 There’s an amusing anecdote about Kay’s choice of the name—he was frustrated by programming systems with grandiose names like Zeus and Thor that lacked substantive function. He sought to establish a playful name that was actually useful.
00:15:54.440 However, Smalltalk was much more than the first object-oriented language. It served as a comprehensive editing environment, necessitating the invention of a modern graphical user interface that included overlapping windows.
00:16:10.270 Additionally, they had to formulate a programming paradigm for the user interface, resulting in the development of the model-view-controller paradigm. It’s not an overstatement to assert that many of our current practices originated from this singular lab at Xerox PARC.
00:16:33.870 Moreover, Ethernet and the modern personal computer were also being developed in the same building. For our discussions, I believe focusing on the object-oriented principles introduced by Smalltalk is particularly fascinating.
00:16:52.680 Ruby is an object-oriented language, and we often have discussions about the most effective techniques available. We even ponder the question of what constitutes object-oriented programming.
00:17:06.320 There may not be a single definitive answer to this question, but we can look into the original intent behind object-oriented programming by exploring Smalltalk.
00:17:27.220 Alan Kay shared a common goal with John McCarthy: both sought to describe computation in a beautifully consistent manner. This is why he appreciated Lisp, yet he had some reservations about it.
00:17:50.290 Remarkably, despite what made Lisp elegant and consistent, Kay believed it lacked a certain robustness. He was concerned that certain aspects, like the 'lambda' keyword, were special cases built into the language.
00:18:11.440 Kay envisioned a leaner core language capable of composing computations of arbitrary complexity. He famously aspired for a syntax that could fit onto a single index card.
00:18:31.320 This led him to rethink how we approach computing. Instead of seeing computers as fragmented units, he proposed a network of interconnected entities, all equally powerful as a complete computer.
00:18:51.280 Thus, in terms of computing, Smalltalk represents a recursion of the concept of a computer. This is the core idea that spawned the object-oriented programming paradigm, and many subsequent concepts flowed from this perspective.
00:19:08.590 So, what does this mean when we execute an operation like 3 + 4 in Smalltalk? This isn’t merely an addition operation; rather, we are sending a message 'plus' to a computer named three, and instructing it to add another computer named four.
00:19:44.300 What that receiver does with your message is entirely up to it, having no obligation towards your expectations. This notion of giving the recipient of a message complete autonomy leads to fascinating potential for dynamic behavior.
00:19:58.730 As I continued my research for this talk, I was astonished by how many constructs in Ruby directly translate from Smalltalk. In Smalltalk, you can send a message to an integer, such as asking it its type, and it will respond accordingly.
00:20:13.930 In Smalltalk, classes themselves are also treated as objects. For instance, you can ask the integer class what type of entity it is, and it will acknowledge that it is indeed a class.
00:20:34.210 The concept of control flow, iteration over arrays, and the handling of message sending in blocks can be traced back to Smalltalk. The meta-programming capabilities we have in Ruby are largely inherited from the Smalltalk vision.
00:21:00.780 For example, in Smalltalk, you can modify the integer class by implementing a method called 'missing method.' This approach is directly related to the principle that the receiver of a message retains full control over its response.
00:21:18.790 It doesn't need to consult a pre-existing table; it can determine its behavior freely. However, some features we didn’t adopt from Smalltalk are also worth discussing.
00:21:34.300 One example is how conditional statements were handled in Smalltalk—there are none. Instead, you'd use a boolean object and send messages like 'ifTrue' and 'ifFalse,' each attached to corresponding blocks of code.
00:21:48.690 This approach can be replicated in Ruby: there are gems that allow you to implement this. Yet, we don’t typically employ this methodology. So, why didn’t Ruby embrace the Smalltalk way of conditionals, you might wonder?
00:22:06.610 I can’t pinpoint the exact reason for this decision, but I’d reiterate that introducing inconsistency thoughtfully can offer more usability benefits. In contrast, Ruby uses a more traditional method for conditionals, which many languages follow today.
00:22:33.790 Nevertheless, we do see a consistent thread of adopting principles from Smalltalk while integrating bits of inconsistency where useful. However, when it comes to the editing environment pioneered by Smalltalk, we didn’t adopt that feature in Ruby.
00:22:56.930 Smalltalk effectively created a unique editing environment, unlike many others in computing history, which hasn’t been replicated today. You can download the modern Smalltalk UI and observe how different it is.
00:23:15.540 In this environment, your code isn’t structured into files but is organized by modules. Within a module, you can view classes and methods can be categorized by related behaviors.
00:23:33.390 This framework allows you to navigate the methods and edit them directly. The remarkable aspect lies in the integration: the code you're viewing is actively running behind the scenes.
00:23:53.370 For example, if you inadvertently alter an attribute in your code, your editor may crash or behave unexpectedly. While this may not seem beneficial at first, there are interesting implications at play here.
00:24:10.430 One fascinating example is during a search for methods to apply to specific inputs in the Smalltalk environment—its integration is remarkable. By simply entering input parameters, it can suggest relevant methods.
00:24:29.270 We didn’t adopt this concept into Ruby, and I think Matz wisely chose his battles. Implementing an environment like this would have posed significant barriers to adoption.
00:24:50.490 I think if Ruby had implemented all these elements from Smalltalk, we wouldn’t have had the same widespread acceptance. It’s intriguing to ponder reintroducing some of this rich editing functionality to Ruby.
00:25:13.210 At its core, Ruby carries numerous functionalities from Smalltalk that could be utilized to create more powerful integrated development environments. What would it look like to evolve our current IDEs into something much more powerful?
00:25:31.520 Now, let’s discuss our final language: Perl, the Swiss army chainsaw of programming languages. As you might expect, Perl presents a markedly different situation compared to the other languages we’ve examined.
00:25:46.960 Perl was developed in the late 1980s by Larry Wall, who worked as a linguist and sysadmin at NASA. Wall wasn’t aiming at formulating a new set of axioms for computation or democratizing computing.
00:26:06.270 Instead, he sought to address practical sysadmin tasks. His idea was to meld the convenience of text processing tools like 'sed' with the capabilities of languages like 'C.' Thus, Perl was born.
00:26:23.030 Perl is characterized by a few catchy slogans that illustrate his pragmatic design philosophy: 'A program is correct if it gets the job done before you get fired.' This captures a keen emphasis on productivity and utility.
00:26:44.400 Wall’s motto 'there's more than one way to do it' resonates within the Ruby community, contrasting with other languages like Python that often emphasize having a singular correct approach.
00:27:02.590 This perspective stems from Wall's background as a linguist. He perceived computation as analogous to human language rather than mathematical modeling.
00:27:16.350 In the realm of human language, various valid expressions exist. Wall integrated this ethos into Perl, and it remains evident in Ruby today.
00:27:34.270 Additionally, he focused on enabling ease of use: 'Easy things should be easy, and hard things should be possible.' This aligns nicely with introducing thoughtful inconsistencies to optimize special cases when they are essential.
00:27:49.780 If you think about it, when you purchase a vehicle, it’s crucial for the chassis, transmission, and wheels to function properly. However, the interior details—such as seating and dashboard design—also significantly enhance the driving experience.
00:28:08.170 Many academic programming languages emphasize the structural integrity but forget about the interior design, which results in a less engaging user experience.
00:28:25.580 Ruby, on the other hand, pays attention to those fine details, resulting in a polished coding experience. Many subtle nuances make Ruby delightful and convenient to use, stemming from Perl's emphasis on pragmatism.
00:28:42.780 For example, Perl has a notably simple string interpolation feature because each variable is prefixed with a specific symbol. This allows you to seamlessly insert variables into strings.
00:28:59.400 While Ruby doesn't have quite the same ease, we still enjoy a fairly nice syntax for string interpolation. I find it quite astonishing how often I encounter languages without efficient string interpolation features.
00:29:15.140 JavaScript, until recently, lacked a clear way to perform string interpolation, demonstrating that these practical considerations matter tremendously. This is an example of Pereira’s ethos with a strong focus on pragmatism.
00:29:34.270 Perl’s contributions to Ruby extend to detail-oriented features, such as native support for regular expressions and convenient array declarations, as well as other conveniences like multi-line strings.
00:29:48.870 Though many languages do not incorporate global variables, Ruby includes them, flagged with a dollar sign, which adds a Perl-like touch. The practicality of global variables becomes evident when you're crafting a quick script.
00:30:08.910 There are peculiar global variables in Ruby with cryptic names, such as one that indicates the process ID of the currently running process. Although some of these quirks are traditionally deemed poor practice, they serve pragmatic purposes when you're hacking together a quick solution.
00:30:27.760 Occasionally, Perl's design leans toward implicit type corrections that may frustrate users. For instance, if you print the string '8' and append '1,' you might receive the answer '9.'
00:30:46.300 In another scenario, if you assign an array's reference to a scalar variable directly, it may unintentionally convert it to the array length. Perl tends to err on the side of implicit conversions, which can provoke frustration.
00:31:01.740 However, Ruby has fostered a more strongly typed structure, which helps avoid some of those troubles. When examining what Ruby acquired from Perl, it gained detail-oriented features without adopting the erratic core.
00:31:09.660 So, this brings us back to the question: why do I love Ruby, and why do we all love Ruby? To me, the answer lies in balance—the harmonious blend of consistent computational models with the flexibility to introduce inconsistencies when such flexibility eases usability.
00:31:36.300 This balance juxtaposes the academic rigor exemplified by systems like Smalltalk and Lisp against the freewheeling practicality inherent in Perl. Ruby is opinionated, particularly with its unwavering commitment to being object-oriented.
00:32:00.510 Yet, Matz strategically prioritized language elements that would promote broader adoption. This balance in design serves as a reminder of how we can blend distinct ideas harmoniously.
00:32:21.520 Many times, we see systems striving to be the most remarkable in some capacity—greatest coverage, fastest speed, or sleekest design. However, I believe Ruby elegantly shows that you don’t always need to be the 'most' anything.
00:32:42.770 Ruby is not the most functional, fastest, or most radical language; instead, it exudes a beautiful mixture of diverse ideas that have been thoughtfully integrated. This insight may guide you as you create your own systems, whether they are programming languages, web frameworks, or products.
00:33:01.440 I hope you’ll take inspiration from worthy concepts and construct a harmonious balance for an exceptional outcome. Thank you.