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.