Jan Stępień

Embrace the static. Cherish the functional. Remain a Rubyist.

This video was recorded on http://wrocloverb.com. You should follow us at https://twitter.com/wrocloverb. See you next year!

We, Rubists, are proud users of one of the most dynamic object oriented language in the industry. We juggle eigenclasses, modify methods at runtime, embrace duck typing and still tend to deliver working software. But there's more than our Ruby out there in the wild. Borders between functional and object-oriented paradigms and between dynamic and static languages aren't as thick as it seems. Several ideas adopted in Ruby originated from communities concentrated around functional languages. Similarly, various tools developed by Rubists are ported to fully static languages such as Haskell. It's hard to overstate benefits of this exchange of concepts. Don't get left behind; see how it can make you a better Rubist.

wroc_love.rb 2013

00:00:18.520 Um, great. So before I start talking about things that are static, things that are functional, and about Ruby, allow me to introduce myself. My name is Jan Stępień, and I come from Wrocław, which is around three and a half hundred kilometers away. I have divided the talk roughly into two parts: the first one will be more technical with code on the screen, and the second one will be more conceptual without any code. Both parts will be about languages, and I am going to make five observations throughout the talk. So, yeah, I’m very happy to begin the day. Without further ado, let's get into it.
00:01:04.880 Ruby, assuming that Smalltalk did not exist, is said to capture the quintessence of object orientation. It is the most we can get out of an object-oriented language. However, that’s not entirely true. Matz himself has repeated many times that his main source of inspiration was, in fact, Lisp, as you can see in the language. Basic operations on the Array class are a direct copy-paste from Lisp. In fact, some of them even use the same verbs. Another example is blocks. We love them and use them everywhere, don’t we? We use them to allocate and release resources, for filtering, and for any kind of loops over collections. The whole ARPEC is nothing but huge piles of huge piles of blocks one inside another.
00:01:39.560 If we define a method or an operator, it means that one language ecosystem—in this case, the Ruby ecosystem—inspired another ecosystem. Hence, we can clearly say that Lisp was an inspiration for Ruby. This is my first observation: Lisp inspired Ruby. Now the question is, is there a reverse relation? Are there languages that were inspired by Ruby? Surprisingly, yes, we do, and they are not the languages you would expect to see in this context. For instance, let's talk about Rack. Rack is a piece of software that you probably know very well; it’s the interface between web servers running Ruby applications. Here we have a simple Rack app that simply returns 'Hello, Rack' with a given header, specifying that it’s text/plain and with an error code of 200. Then we launch it on port 9292.
00:02:39.760 Now I’m going to modify this code a little bit. Tell me whether you see many differences. Has a lot changed? I don’t think so. In fact, it’s quite similar— the same API, the same principles, the same abstraction over HTTP. The joke is that now we've entered the world of Clojure, a pure Clojure list for JVM. The formatting is not fully Clojure; white spaces should be rearranged a bit. This is way better, and as you can see, this is a direct port of Rack. This code uses the Ring library, which was heavily inspired by Rack. As you can see, certain concepts which were developed in the Ruby ecosystem fit well.
00:03:00.720 I’ll gladly take questions after the talk if I can. Let’s consider another example: RSpec. Let’s examine a simple example that checks whether the addition of doubles works as expected. Once again, I’m going to modify it slightly. Not that different, is it? Well, seemingly, this is pure Haskell—functional, statically typed, lazily evaluated. This is everything that Ruby is not. The functionality is quite the same. It uses the HSpec library, a port of the Ruby concept to the world of Haskell. It fits well and is used, at least from what I’ve heard from some high school people.
00:04:12.880 So, the second observation is that solutions which were developed in our community have migrated to other communities. I'd like to underline that my point is not to say that Ruby is so awesome because it inspired many other languages; that’s not my point. However, it’s not far from the truth. My point is that there are tools which solve certain problems well in one area that, when ported to completely different environments, work equally well. So let’s talk about those tools that solve certain problems in other languages far from the Ruby ecosystem and see how they apply in a different environment.
00:05:10.919 Let’s return to the RSpec example. Let’s say we carefully implemented the division method just as Tim showed us in the case of Ruby, and we want to test it. This test doesn’t give us much; we are only certain that 1.5 divided by 3 yields half. Not much. So let’s change it to add parametric testing and cover a whole range of examples. It might be a bit better indeed, but of course, there are edge cases, right? Edge cases you can specify by hand: all of them, and specify what behavior you expect. But it’s tiresome, isn’t it? So let’s see what a different approach would look like.
00:06:11.160 We go back to RSpec. Let's directly rewrite the Ruby example, and now we make a change in the last line. Instead of specifying a concrete input and concrete output, we specify a property that we expect the tested function will hold. In this case, the property is specified by a lambda expression or a block. It takes two arguments and returns the value of this expression. If we run it, we get the following error: the testing library generated automatic inputs for our function, and it found out that for this input, it fails. That was expected. So let’s modify our test a bit.
00:07:00.080 We cannot do anything with division; it simply is not defined for zero. So instead, let’s watch the last line. We introduce a rule that we are interested in the result only if B is different from zero. Wonderful! Let’s test it again. Another error occurs. The reason? Well, floating-point operations aren’t precise, are they? If we launch GHCi, which is the Haskell equivalent of IRB, indeed, it turns out that it’s not three, in fact… Thank you.
00:07:53.760 Now, let’s think about what happened. If we as software engineers assume that floating-point division will yield an exact result, we should be fired. And thanks to this library, our job is saved! Quite useful, right? It generates tests for us; we don’t have to think about test cases to a certain extent. Of course, it's not completely automagical. The name is QuickCheck. It’s been on the market for over thirteen years, and it's been production ready long before Rails went public. You might think, okay, awesome, but we speak Ruby, right? This isn't quite a high school conference, so why should we care? Because our language provides tools that allow us to port this concept and use it in Ruby without any problems.
00:09:11.040 Is there a catch? No, there’s not a catch; it’s been done already. It’s called RSpec. You can gem install it, and here’s an equivalent. I begin by including a module to make it less verbose. Then I specify a property. Here, I specify that I expect two floats as input, and here they are assigned to variables. This should be true—or the test fails. Then I execute the test and get the following result. RSpec managed to falsify my assumption with the following input as expected.
00:09:59.040 Let’s introduce a similar change as in the Haskell version. Watch the body of the block: we introduce a guard saying that if Y is zero, we are not interested. Keep on generating new inputs.
00:10:02.560 Wonderful, it’s wrong again because of floating-point inaccuracy, right? Time for some cold realism. At this point, it's a lot of fun to do some weekend testing on simple data structures, however, in a 9-to-5 job, that’s not quite what we are doing, is it? No problem, RSpec provides an API which allows you to specify how you want to generate arbitrary instances of any Ruby objects. Be it your ActiveRecord objects or whatever. Based on its existing generators of test cases, which generate floats, strings, etc., you can generate arbitrary instances.
00:11:02.080 Therefore, I claim that the Haskell ecosystem managed to put a bit of its inspiration into our world and it’s good. It creates new options and new possibilities. This would be my third observation—that ideas from Haskell work surprisingly well in our ecosystem. I think that at this point we are quite tired of testing, so let's shift to another topic, which is quite popular during this conference. Let’s talk about concurrency.
00:12:26.560 Concurrency is a problem that has seen various solutions, various approaches. I’m going to talk about one of them—a Swedish one—about Erlang. Erlang is built upon two things: firstly, a rock-solid virtual machine, and secondly, a set of libraries in the standard library of the language. As long as your problem is more or less related to client-server communication, distribution, peer-to-peer stuff, it is most probably already solved in the standard library. You could argue that Erlang is simply a Domain Specific Language (DSL) for concurrency.
00:13:15.560 The virtual machine is quite strange, and without it, the whole system would not work at all. In this virtual machine, we have basic operations such as spawning and linking processes, sending and receiving messages. In fact, sending messages was so important that it was renamed to an exclamation mark because logical negation was less important. Awesome! We’ve got a very powerful virtual machine which allows for good concurrency. However, in Ruby, our virtual machines are not quite as parallel as the Erlang one, are they? No problem! We can just port concepts and ideas, not the underlying virtual machine.
00:14:10.120 First, allow me to address any lingering questions you may have: does anyone actually use Erlang? Does it work anywhere? Has anyone here played any Call of Duty titles in multiplayer? Hands up. Wonderful! The matchmaking system and the multiplayer are done in Erlang. The backend that handles multiplayer in Call of Duty: Modern Warfare is all Erlang. Those of you who haven't played Call of Duty: Do you have Facebook accounts? No? No way? Me neither! But I have played Call of Duty. Facebook chat? It’s Erlang!
00:15:18.720 I hope that this is more convincing and a better proof that Erlang is used in production code to solve massively concurrent problems. The ideas upon which Erlang is based have already been introduced during this conference. Hello! Celluloid takes ideas from the Erlang VM, wraps them with Ruby idioms, and gives us ready objects. So let’s take a look at an example: the capital is the main city of a country. In the case of Poland, currently, it’s Wrocław. However, given the quality of this conference, it might make sense to move the capital to Warsaw. Sorry for that.
00:16:34.320 Beforehand, we have to decapitalize Wrocław, so let’s write a method for that. Wonderful! Now we can create a capital. Let’s name it Wrocław and decapitalize it. Wrocław is not, surprisingly, disappointing. The process of decapitalizing a capital takes time; it takes money. It’s expensive; we do not want to do it in the main thread. Let’s do it in the background. This will require certain changes. Let’s include the magical Celluloid module and then introduce an instance variable, which will make it easier to see that the state of the capital has changed.
00:17:39.120 Apart from that, I’m setting the capital to true here and decapitalizing it to false. Wrocław is still disappointing. Wonderful! We create an instance. This time you see that it’s wrapped in an actor, a Celluloid actor. If we decapitalize it normally, we get the disappointing message. Wrocław is indeed not a capital any longer. Now, let’s move it to the background with a subtle change. We add an exclamation mark. Remember, in Erlang, the exclamation mark was sending.
00:18:36.640 We get nil because once we fire a method asynchronously, it's a fire-and-forget. We send a message, and we are not interested in the result. We’re not interested that Wrocław is disappointing. Still, Wrocław is not a capital any longer. However, you know, Wrocław can be really disappointed. That disappointment will raise an exception while being decapitalized. Now, this is a problem. If we decapitalize without bang, we hit a runtime error, just as expected. Now we introduce exclamation mark: the error is thrown in a separate thread; it is logged, and if we don’t care, we will never know about it.
00:19:45.760 We are in our main thread in which we called the capitalized bang, we get nil, and everything moves forward without any exceptions. We don’t care that Wrocław is disappointing. However, from what I saw here in Warsaw, it is a nice city; people are kind. I meant Warsaw, sorry.
00:20:56.080 Let’s introduce certain modifications. This didn't change much; I created a second city, which is also a Celluloid actor, and this is where things are getting interesting. We inform that the exit trap method informs the runtime environment that once another actor, linked to the current actor, dies, this method specified by this symbol should be called. Let’s see how it works in action. We spawn a capital, we spawn a city, and we link them together.
00:21:57.200 Now, once we decapitalize asynchronously, we get a runtime error in a separate thread, and in yet another thread, the Wrocław actor is being notified about the exception. Yet in the main thread, nothing happens; we still get the nil value, and we carry on. So as we can see, a lot of things from Erlang were ported, despite the lack of an underlying solid virtual machine.
00:22:55.600 This is my fourth observation: ideas from Erlang work great in Ruby. Let's reflect for a moment. The good news is that this is the end of part one—no more code from now. I would like to take the opportunity to say that I personally like Warsaw a lot and I appreciate the people there. I encourage you to pay a visit; it’s a wonderful place. No hard feelings at all; I can only recommend it as a native Varsovian.
00:23:50.960 Let’s move to part two; let’s talk about natural languages. They are fascinating—there's plenty of them, and they express ideas in completely different ways. This is a Polish proverb which translates verbatim to something like this, expressing disappointment that people are mean to each other. Observe that in the Polish version we use just three words— in fact, there is no verb at all! You see that the way of expressing thoughts is completely different, right? Noun-oriented, verb-oriented, modifying words, or adding small words here and there to modify the meaning.
00:24:30.120 To go further, there’s a thing called linguistic relativity, which says that the language we speak affects how we think and how we formulate thoughts. This was introduced for natural languages; however, in a talk delivered in 2003, Matz said that he, as a Japanese speaker, has serious problems expressing himself in English. When he speaks Japanese, he’s more polite, more emotional, while in English, he is straight to the point, blunt. This was his first point. He then said that this applies to programming languages.
00:25:31.760 He showed examples that proved, according to him, that if you remove the word natural from here and replace it with programming, it works perfectly. Behold the averages; there’s a very good essay published two years earlier by Paul Graham. In this essay, he proposed a scale of languages, focusing on the expressiveness of programming languages. There was a programming language named Blob, where programmers who used it were proud because it was more expressive than assembler, more expressive than Pascal. Yet when they looked upwards to the language F, they didn’t understand it: strange syntax, strange constructions; it made no sense at all.
00:26:24.960 Now let’s move a bit to the right: the F programmer is happy because he’s proud of his language, which is in a way superior to Blob. However, when he looks the other way, he sees F# as a mess. Why do you need all this type syntax, right? This essay can be summarized with a following sentence taken from the Pragmatic Programmer: Learn a new language each year; develop your portfolio. This is my fifth observation: polyglots, both in natural and programming languages, have an advantage because they have a wider perspective.
00:27:14.560 I prepared a short checklist for you. After you return home, please download a different programming language. Python is not a different programming language. Find things that are not obvious—not clear. Then try to understand them. Try to get the point: why is it there? How can it apply? Then try to port it. Understand whether it would be good to port it to Ruby. Then apply it in a gem or write a blog post. Or at least, on Twitter.
00:28:00.080 However, last but not least, send your findings to me. I’d love to know what you think about it. Seriously, I love receiving mail—apart from offers; you know what I’m talking about.
00:28:37.520 I’d like to end this presentation with a quote that comes from mathematics; however, I think it applies wonderfully to our discipline. Let me read it for you: 'By relieving the brain of all unnecessary work, a good notation sets it free to concentrate on more advanced problems.' I hope that now the topic of my talk is more clear to you. Embrace the static, cherish the functional, and remain a Rubyist. That’s all I’ve got. Thank you all very much!