00:00:15.650
How about one more round of applause for the great conference? It's been a fantastic event, and I'm really enjoying it.
00:00:20.910
Let's give it up for these guys: Brian France and Cory Stephen, for making this conference so great.
00:00:27.090
I know this is the Gotham Ruby Conference, but what you see on the screen is not Ruby. It's actually a functional programming language called Haskell.
00:00:32.430
Are there any Haskell programmers in the room? I see a couple of hands waving in the back. Can anyone tell me what this code does? Does anyone recognize this algorithm?
00:00:39.510
Perfect, good job! This is the quicksort algorithm. Don't worry, I’m not going to explain this algorithm in detail.
00:00:45.780
So why am I showing this to you? I think it's really important for all of us to look at other programming languages from time to time. We should learn from other languages and not just use Ruby every day.
00:00:58.230
If we always do the same things that we did before, we're not going to learn much. There are a couple of reasons to study other programming languages. The first one is really obvious: when you step outside of your comfort zone and tackle a different learning curve, you learn a lot of new stuff quickly.
00:01:10.140
The second reason is less obvious. To illustrate this point, let me draw an analogy between programming languages and human languages.
00:01:15.630
Just like learning a foreign language, such as Chinese, Greek, or in my case, Spanish, has many benefits, learning other programming languages can provide similar insights.
00:01:26.820
I was lucky enough to marry a woman from Spain, and later in life, I learned Spanish as an adult—it was one of the most rewarding and exciting things I've ever done.
00:01:32.340
One summer, we took our kids to Spain to spend time with her family. While we were there for a few weeks, or even almost two months one year, I immersed myself in Spanish, as none of her family spoke English. I got really used to the staccato way they spoke over there.
00:01:48.270
After a while, I heard English again—maybe I was walking down the street and passed by a tourist, or maybe I called home. It was the first time I had heard English in six or seven weeks, and it sounded a bit different to me.
00:02:00.640
This experience gave me some perspective on my own language and how it sounds to a non-native speaker.
00:02:10.140
In the context of programming, this Ruby conference represents our native language. If you study another language, when you return to Ruby, it might seem different to you. You may begin to use Ruby in a different way.
00:02:40.120
Where does Ruby come from? This is Matz, the founding father of Ruby. Did he just wake up one day and decide to create a new programming language? I'm not sure. Matz isn't here today so we can't ask him.
00:02:56.200
I did a bit of research and found an interesting quote from a Ruby talk mailing list back in 2006. Many of you may have seen this already.
00:03:02.080
This quote provides significant insight into where Matz got the ideas for Ruby. He looked at several programming languages and did what I mentioned earlier—he examined them for inspiration.
00:03:10.350
He specifically mentioned taking a simple list language, adding methods from Smalltalk, and including functionality found in Perl.
00:03:18.730
Matz explored several programming languages available at the time, pulled in different features he wanted to incorporate, and created the new language that we recognize as Ruby today.
00:03:27.460
If Matz is the founding father of Ruby, then perhaps the creators of Smalltalk, Lisp, and Perl are like the grandfathers of Ruby.
00:03:38.600
This is Larry Wall, who created Perl in the late 1980s. While it might seem like a long time ago, it wasn't that long.
00:03:44.000
I don't know much about Perl—I’ve only used it here and there—but I know that a lot of Ruby's regular expression syntax comes from Perl.
00:03:51.430
Another potential grandfather is Alan Kay, who created Smalltalk back in the late 1960s. This was over 40 years ago! If you haven't checked out Smalltalk before, I encourage you to do so.
00:03:57.580
It's an amazing system that includes its own graphical user interface and an integrated visual debugger, cutting-edge for its time.
00:04:04.270
One of the remarkable things Alan and his colleagues did was to take computers designed specifically for Smalltalk and bring them into middle schools to teach programming concepts to kids, perhaps for the first time.
00:04:11.290
The entire language was also designed with education in mind. Alan was a real visionary; he conceived of the idea of a laptop in the 1970s, 15 to 20 years before laptops were built.
00:04:18.100
He referred to it as a Dynabook. I encountered an interesting diagram about Smalltalk in an academic paper around six months ago.
00:04:25.080
This diagram stood out to me; it essentially depicts how Ruby functions. It illustrates the internal class hierarchy of Ruby, including the class class, object class, and integer class.
00:04:31.960
However, if you look closely at the caption, you'll see it refers to Smalltalk's 76 metaphysics. This diagram is nearly 40 years old and represents a language that is not Ruby.
00:04:38.820
It's remarkable how much of Ruby derives from Smalltalk. At its core, Ruby is fundamentally an object-oriented programming language.
00:04:46.520
As Rubyists, we are all object-oriented programmers, but there's one more language Matz mentioned in that quote that we need to explore.
00:04:57.940
Wait, I've seen this picture before—this isn't the first slide! John McCarthy is behind everything that’s happening here, isn’t he?
00:05:07.360
Yes, this is John McCarthy, who invented Lisp back in the late 1950s. If you don't know about Lisp and want to learn more, check out this classic book.
00:05:13.090
Lisp is famously known as possibly the first and most influential functional programming language.
00:05:19.780
It was invented in the late 1950s, and the paper I mentioned earlier discusses symbolic expressions, defining the whole concept of functional programming.
00:05:27.660
This brings me to my main topic for today: functional programming.
00:05:31.920
I want to discuss what functional programming is, whether we can achieve that with Ruby, and what concepts Matz might have borrowed from Lisp.
00:05:42.290
I won't delve into Lisp in detail, but I will introduce you to Haskell. This is the original code we had earlier—quicksort.
00:05:49.610
Haskell is a concise and beautiful language, and I've been studying it over the past few months.
00:05:57.030
Haskell is actually named after Haskell Brooks Curry, a 20th-century mathematician who studied lambda calculus, signified by the Greek letter lambda that represents a function.
00:06:03.530
So what is Haskell? It's defined as a polymorphically statically typed lazy purely functional language, whatever that means.
00:06:10.520
In contrast, Ruby is an object-oriented dynamic language. Ruby and Haskell are two very different languages with little in common, but today I aim to compare Ruby and Haskell and illustrate their differences.
00:06:30.340
To start, I want to take a couple of minutes to define functional programming. Many of you may be familiar with it, but some might not have a clear understanding.
00:06:42.680
Let's clarify this to get on the same page, and then I'll discuss three techniques that functional programmers frequently use.
00:06:51.920
I'll first explain these concepts, demonstrate them in Haskell, and subsequently examine how we can apply the same principles in Ruby.
00:07:03.330
These techniques include higher-order functions, lazy evaluation, and memoization.
00:07:09.170
Let's start by discussing functional programming, which is simply programming with functions.
00:07:13.760
Functions have a domain and a range; you input a number X, which goes into the function and is processed to yield an output of f(X).
00:07:18.430
The principal concept behind functional programming is that a pure function, when given the same input (in this case, X), will always produce the same output (f(X)). Functions are independent of time; it does not matter when I call this function or how often I call it.
00:07:42.860
The benefit of adhering to pure functions lies in their predictability, which helps you write better software. If I construct an application with distinct functions that always return the expected output, the software will become more robust and reliable.
00:08:05.510
However, writing pure functions can be challenging since there can be no changing states or memory used within the function.
00:08:10.960
The function must remain isolated without side effects and cannot interact with the rest of the application. Essentially, the functions operate as black boxes.
00:08:31.420
Despite their complexity, the potential benefits of functional programming are substantial. If you want to delve deeper into functional programming's philosophical underpinnings, I recommend checking out Rich Hickey's presentations.
00:08:44.370
Rich Hickey is a brilliant individual who created the language Closure, a dialect of Lisp that operates on the Java Virtual Machine.
00:08:53.470
Hickey is also a captivating public speaker, and his videos are worth watching as he discusses profound concepts about state, time, and data in a compelling manner.
00:09:05.060
Now, I want to shift gears away from philosophical discussions and direct our focus towards practical coding examples, starting with higher-order functions.
00:09:12.510
When we speak of higher-order functions, we're referring to functions that either take other functions as parameters or return another function as their result.
00:09:31.139
Let's look at a specific example in Haskell where we create a list with a series of numbers.
00:09:37.520
In Haskell, you can define a list simply as [1, 2, 3]. You can also create a list from a range, similar to this: [1..10].
00:09:47.800
In Ruby, we achieve a similar outcome by utilizing arrays or ranges. For example, I can use (1..10).to_a.
00:09:55.040
While at first glance Haskell and Ruby may appear somewhat alike, let's dive deeper and see how to calculate the first ten squares.
00:10:02.560
In Ruby, we can achieve this using the syntax: (1..10).map { |x| x * x }.
00:10:09.900
In Haskell, the equivalent code does employ a slightly different syntax; we express it as [x * x | x <- [1..10]].
00:10:15.220
Although the language structures are different, you can observe that they function relatively similarly despite originating from distinct paradigms.
00:10:23.170
Now, let's define what higher-order functions are. We just explored an example of a higher-order function when we used the 'map' operation.
00:10:30.570
A higher-order function is one that accepts a function as an input and can return a different function as output.
00:10:36.820
In Ruby, I can show you this in a more verbose way by explicitly passing a lambda as an argument. This highlights that we're working with functions as inputs.
00:10:43.420
For example, in this Ruby line, I'm taking (1..10).map(&:lambda) and using lambdas to define the operation I'm applying.
00:10:52.120
In Haskell, you could achieve this behavior by defining a similar mapping function that takes another function as an argument.
00:11:03.420
Both languages have the functionality to work with higher-order functions, highlighting that, while the syntax differs, the underlying principles share striking similarities.
00:11:11.370
Next, let's explore the philosophical approach that functional programmers adopt in contrast to object-oriented programmers.
00:11:21.530
As object-oriented programmers, we often conceptualize problems in terms of interacting objects and their respective states.
00:11:28.060
However, functional programmers focus more on their data—how it is processed and transformed through a series of functions.
00:11:36.390
For instance, if I want to compute x squared plus one, I might apply a function to obtain the intermediate and final result step-by-step.
00:11:46.820
In Ruby, you'd likely implement the same logic through chaining methods together logically. As we analyze how each language processes the same input, we'll notice subtle differences.
00:11:57.720
Let’s consider how each language processes a series of numerical transformations by applying multiple functions in succession.
00:12:06.850
In Haskell, you may have an elegant way of combining these transformations using function composition, yielding a straightforward result.
00:12:15.560
By applying higher-order functions, such as a series of 'map' calls, you'll see that Haskell performs operations in a functional pipeline.
00:12:22.630
In contrast, Ruby allows you to chain the transformations using similar methods. Both languages return the same results, but the underlying mechanics differ subtly.
00:12:29.490
The abstract understanding of functional programming principles can enhance how you think about Ruby's own paradigms.
00:12:36.550
Let's delve deeper into the next core idea of lazy evaluation. Before we proceed, we should understand the critical aspect of what lazy evaluation entails.
00:12:45.840
In Haskell, I can define an infinite list of elements, which allows me to access elements on demand without pre-calculating them upfront.
00:12:55.860
When utilizing lazy evaluation, Haskell waits until values are actually needed before computing them.
00:13:03.750
For instance, I can declare an infinite sequence using the expression [1..] and obtain values on-the-fly when requested.
00:13:12.250
This concept highlights the power of Haskell's lazy evaluation and how delayed computation can enhance performance and memory usage.
00:13:19.390
Now, can we achieve something similar in Ruby? With the release of Ruby 2.0, we introduced a feature called Lazy Enumerator.
00:13:27.230
I can use a similar approach in Ruby, but it requires a bit more typing than in Haskell.
00:13:35.140
Using the expression (1..Float::INFINITY).lazy, I initiate an Enumerator and chain operations efficiently.
00:13:45.900
The balance between ease of use and performance involves a trade-off; Ruby’s syntax is more verbose and, by default, is eager in comparison to Haskell's inherent laziness.
00:13:58.520
The Lazy Enumerator allows us to efficiently process sequences without evaluating all elements at once.
00:14:06.030
When using the 'collect' method on a Lazy Enumerator, the operation will not compute until we explicitly request the computation with methods like 'first' or 'take'.
00:14:21.350
The essence of lazy evaluation lies in controlling computation until the very moment it's needed, enhancing memory efficiency.
00:14:36.710
Next, let’s discuss memoization, a programming technique used for caching results to enhance performance. Think of it as an optimization method.
00:14:55.160
A classic example of where memoization can be beneficial is the Fibonacci sequence, known for its recursive calculation.
00:15:04.450
The nature of computing Fibonacci values traditionally leads to exponential time complexity, especially when invoked recursively.
00:15:12.780
In Haskell, I might implement the Fibonacci sequence in a simple but inefficient manner using recursion. The slow Fibonacci function computes the same values repeatedly, leading to sluggish performance.
00:15:27.160
To remedy this, we can incorporate memoization. By storing the computed values, we avoid calculating them multiple times, leading to optimized runtime efficiencies.
00:15:39.700
When defining a memoized Fibonacci function in Haskell, I can leverage Haskell's capabilities to define recursive functions naturally.
00:15:51.490
I can define a slow 'fib' function with specific cases for 0 and 1 and a catch-all for all other values using recursive pattern matching.
00:16:01.750
The benefit of this approach is that Haskell allows for elegant recursive definitions that leverage previously computed values.
00:16:08.050
To illustrate, I can use memoization and define my Fibonacci sequence efficiently. It's a straightforward method in Haskell, taking advantage of lazy evaluation and recursion.
00:16:21.210
The way functional programmers often conceptualize this is by constructing infinite lists of values, where all Fibonacci numbers are generated by mapping the recursive definition.
00:16:37.060
This paradigm shift allows for cleaner, more efficient code structures that serve as caches for computed values, streamlining the process.
00:16:48.840
Now, let's explore how we might tackle this in Ruby. Although Ruby does not lend itself to lazy evaluation and memoization as straightforwardly as Haskell, we can adapt similar patterns.
00:17:02.420
In Ruby, implementing caching can be achieved through a hash data structure, allowing values to be stored once computed.
00:17:12.890
Utilizing the ||= operator, I can easily build a memoized Fibonacci function without the same elegance as Haskell but still with effective results.
00:17:24.560
In conclusion, while Ruby isn’t a purely functional language like Haskell, it still possesses functional programming features that can enhance the way we approach coding.
00:17:39.160
Today, I encourage all of you to take time away from Ruby and delve into other programming languages. Even exploring human languages can foster new insights.
00:17:53.080
Your experience with other languages offers you a fresh perspective when you return to Ruby, which might encourage you to use Ruby differently.
00:18:01.590
Remember, Ruby offers many functional programming features. Matz indeed drew inspiration from Lisp, especially in elements like blocks.
00:18:09.070
Although Ruby is not a pure functional language like Haskell, it provides room for both object-oriented and functional programming paradigms.
00:18:20.750
That's it from me today. If you're interested in learning more about Ruby internals, check out my book entitled "Ruby Under a Microscope," which I am currently revising for print.
00:18:33.580
Now, we have a few minutes left for questions. If anyone has any inquiries, feel free to ask, and I will do my best to answer them.