Metaprogramming

Summarized using AI

Ten Things I Hate About Ruby

Hal Fulton • August 11, 2011 • Earth

The video, titled "Ten Things I Hate About Ruby," presented by Hal Fulton at the LoneStarRuby Conf 2011, examines various aspects of the Ruby programming language, highlighting its imperfections and counter-intuitive features. Fulton's aim is to provoke thought and discussion regarding Ruby's design elements that could be improved.

Key Points Discussed:

- Global Variables:

- Ruby inherits global variables from Perl, which include confusing symbols (e.g., $!, $, etc.).

- These globals are rarely used and can lead to side effects, prompting a desire for a culture that avoids them.

- Code to Objects Conversion:

- Various ways to convert code into objects exist, causing confusion even for experts.

- Differences between Proc, Lambda, and the newly introduced 'stabby lambda' can complicate coding for many users.

- Reflection Confusion:

- The reflection system in Ruby complicates understanding which methods belong to which classes and their accessibility (public, private, protected).

- Method Arity:

- The ability to determine method parameters is inadequate, as Ruby doesn't provide sufficient support to know how many parameters a method requires.

- Ruby Gems Complexity:

- Discrepancies in requiring gems (historically varied practices) lead to confusion on different systems and setups.

- Syntax Problems:

- Elements of Ruby syntax, like class appending with self and rescue notation (=>), are often cumbersome and confuse developers.

- Ordered Hashes:

- Although Fulton personally doesn't use ordered hashes, he acknowledges their practicality for certain use cases.

- Balancing Syntax:

- A discussion on whether Ruby has too much or too little syntax, emphasizing the importance of clarity in programming notation.

- Keyword Arguments & Hashes:

- Fulton shares frustrations regarding the use of hashes as substitutes for keyword arguments and wishes for a more elegant solution.

Conclusions and Takeaways:

Fulton concludes by emphasizing that while Ruby has many strengths, awareness of its weaknesses and potential improvements is vital for developers. Each Rubyist may have their own set of challenges and preferences, which could lead to further discussions and ideas for the language's evolution.

Ten Things I Hate About Ruby
Hal Fulton • August 11, 2011 • Earth

We all love Ruby. But let's face it: It isn't perfect. We each may have a personal list of complaints. Yours will vary; this is mine. These are the things about Ruby semantics and the Ruby core that I find counter-intuitive, difficult to remember, incomplete, improperly designed, or not quite adequate. There might even be one or two notes here on syntax, the most fundamental level of any language. And "hate" may be too strong a word; but once in a while, we have to ask what is lacking in our favorite tool, so that someday when replace it, we will have something better (whether that thing is called "Ruby 2.0" or something else entirely).

Help us caption & translate this video!

http://amara.org/v/FGdy/

LoneStarRuby Conf 2011

00:00:21.080 I picked the topic 'Ten Things I Hate About Ruby' because it was evocative of that movie from the '90s, you know, the one that stole its plot from a Shakespeare play. It's an entertaining movie—not a bad movie if you get the chance to see it, but don't bother. As for 'ten things,' it depends on what base system you're using. You may count as few as three or four things in my slides, or you may count as many as twenty-five. Your mileage may vary.
00:01:07.720 Anyway, the first thing that I hate about Ruby—and maybe you already know this—is the global variables that are inherited from Perl. By 'global variables,' I mean those that look like the way comic strip characters use profanity. They're all punctuation marks: dollar sign, slash, dollar sign, exclamation point, and so on, including some Unicode characters, I'm pretty sure. First of all, those variables are inherited from Perl, and we don't need them in Ruby. I think you'll agree that there's a fairly thick book called 'The Ruby Way,' which makes very little use of those global variables. You probably cannot find more than half a dozen instances in 850 pages where they're used as far as I recall. So they're unnecessary. Global variables—well, they have the purpose of side effects, right? We learned at some point in our education or careers that side effects are bad, and to me, these are especially bad because they change the innate behavior of the core methods that we're used to.
00:02:25.879 For example, who can tell me what dollar slash is good for? What effect does it have? It’s like a line terminator or separator. So, things that expect a certain kind of terminator behave differently based on how you set that thing. And while there’s no question that sort of thing can be useful, if you absolutely have to use it, then use the English equivalents. You have this library called 'require English' that gives you named aliases for all those globals. But what I would really like to see in Ruby is a culture where we don’t use those globals. I don’t think they're ever necessary. The most common time that I use them is in regular expression matching. I'll use dollar one, dollar two, dollar three to match the pieces of a regular expression that matched. You know, especially in earlier versions of Ruby, that made a lot more sense. In 1.9, it doesn’t make as much sense because you can give names to the individual pieces of a regular expression that you're matching. Named entities are better than just saying first, second, third, and so on for items matched.
00:03:39.799 That’s probably my number one thing that I hate. I also don’t understand why there are too many ways to turn code into objects, and there are subtle differences between many of these—sometimes not so subtle differences. I, who am supposedly a Ruby expert, have trouble keeping track of all the details. It may be that many of you out there are smarter than I am and can easily keep track, but I tend to use the simplest thing that works. I don’t use any elaborate tricks. But take Proc and Lambda, for example. Those have sort of changed; they've flip-flopped back and forth. I'm not sure one of them is even used anymore. Do we still use Proc in 1.9? Does anybody know? Does anybody care? Okay, it still works, but you don’t see it as much, at least according to one person. That implies that it may be falling out of fashion.
00:05:09.800 At one point, I heard that we were going to start using Lambda instead of Proc. At another point in time, I heard we would have both but they would have slightly different semantics. However, I don’t think that lasted very long. As if it weren’t enough to have all these options, we also have the 'stabby lambda' now, so named because it looks like the left-hand side of the expression is attacking the right-hand side. Most of these exist for good reasons, but it would be nice to see a bit more unification among them. And while we’re at it, there’s no kind of code block in Ruby that is not a closure. I don’t know if anybody cares about that except me, but during metaprogramming, I have often found myself forced to use a string instead of a block because I didn’t want the context of the block carried along with it.
00:06:29.000 You see, there are times when it can result in what one person called a memory leak. It’s really not a memory leak, strictly speaking, because Ruby is just doing what you told it to. But the point is, a code block in Ruby always carries its context with it. Any variable visible within that block is known in whatever context that block has used from then on. You can't forget that context if you want to. There have been times I wanted to forget it.
00:08:30.138 Next, personally, I find that reflection is confusing. Let me see if I can get more of this on the screen here. I don't know why that didn't wrap properly. Let's take the instance methods method, for example. It behaves as follows: this is the description I got from rubydoc.org: returns an array containing the names of the public and protected instance methods in the receiver for a module. The methods returned are instance, not singleton methods, and they have no argument or have an argument that is false.
00:09:50.919 The instance methods in the module are returned; otherwise, the methods in the module and its superclasses are returned. You understood all of that the first time, right? That, to me, is kind of the way reflection works in Ruby. I have trouble remembering whether something is simply called methods or instance methods. Should I call it on the instance or should I call it on the class? If I want to get private instance methods, I’m pretty sure I’ll only get the private ones, but if I don’t specify private or public, what do I get? And is protected included in that? It just confuses me. Maybe you’re smarter than I am, but it confuses me.
00:11:00.679 Another thing is that the method arity is not adequately supported. I think there is some provision for knowing how many parameters a method takes if you use method.new, but otherwise, I don’t think there’s any provision for that. You can know the names of all the methods in a class or an object, but you can’t know how many parameters each of them takes. I think that's a failing.
00:11:55.799 Next, Ruby gems: there's been some flip-flopping back and forth over the years regarding how Ruby gems actually work. There was a time when we were told to set an environment variable that said - ruby gems.
00:12:07.199 So, effectively, it was doing a dash 'require' of a library called Ruby gems. There was a time when we were told to just put 'require ruby gems' at the top and you’d be fine. Then it got to a point where we were told you don’t have to put 'require ruby gems' at the top anymore; just run everything and it’ll be fine. I don’t know if your experience is different from mine, but it seems that whenever I move from my computer to someone else's, there are subtle differences in behavior. It may be because I am switching between Ruby versions or because people have things set up slightly differently. But I’ll find that on one machine I have to require Ruby gems and on another I don’t. So do I or don’t I? Are gems really integrated into Ruby or are they not?
00:13:19.600 With RVM and Bundler, we’ve seen some improvement in that respect, but there's still some basic confusion in my mind. As I said, your mileage may vary. There are certain pieces of syntax in Ruby that I find ugly or strange, often because I don’t have a good way of understanding what they mean. Like the class append self notation. When I was learning Ruby, I just learned what it did and I understood what it did. I was fine, but in the back of my mind, I always thought: What is the double less than for? What does this mean? Did anybody else ever have that feeling? Some of you? Yeah, the rest of you are too smart to care.
00:14:17.000 But it always bothered me because the purpose of any notation is to clarify your thought. I don’t like bumping into things like that, which don't clarify my thought but instead confuse it. I never understood the double less than notation. Even now, I prefer to say 'define self.method_name' instead of a less-than sign. There are probably subtle differences, but not many. The so-called stabby lambda notation has had actual arguments on the Ruby mailing list over whether this was a good thing or not—not the semantics, but just whether a hyphen followed by a greater-than sign is a good or bad thing. I have to say, to some extent, I understand that, because for one thing, I don’t know how to read it.
00:15:41.839 If I were to read a program out loud, as I sometimes will when sharing with a colleague, I wouldn’t know how to pronounce that. It just looks wrong to me; it reminds me of C. I have many bad memories of programming in C, and I can’t count the thousands of times my finger typed a hyphen followed by a greater-than sign. I don’t want to relive those moments.
00:16:00.000 Another minor point that always bothers me a tiny bit is the rescue notation. I find it strange that we’re using equal greater-than there, which is not used anywhere else except in hash notation. So every time I see that, I think I'm looking at a hash. Moreover, this is also the only place where we have the source and destination reversed. We’re used to assigning the right hand value to the left hand name; however, in this case, we’re essentially assigning the left hand value to the right-hand variable name. It’s kind of a reverse assignment—the only place in Ruby where you’ll see that. And the only place in Ruby where you’ll see equal greater-than outside of a hash. So once again, there are a couple of neurons in my head that itch when I see that.
00:17:32.560 Now, you probably have your own set of things that you find confusing, counterintuitive, ambiguous or whatever. So I’ll open it up for questions, but not only questions—comments if you have any. Not everybody at once! So, yes, how do you feel about ordered hashes? I was one of the few people who wanted them introduced, and I think it was Nobu Nakada who made an implementation and proved that they didn’t have any real impact on efficiency because you’re just keeping a little counter along with each key value pair. The reason I like them is that if you wanted an ordered hash, now you’ve got one. If you don’t want it, just pretend it doesn’t have an order, and you'll be fine.
00:19:20.640 Seriously, because even in earlier versions of Ruby, when you iterated over a hash, it would come out in some kind of order, and the same hash would always come out in the same order. It’s not as if you had any genuine randomness. If you need genuine randomness, you can implement it. If you want order, you’ve got it. I actually had a few practical applications for that, for example, when I had a kind of state machine and a bunch of key value pairs that I would index off of a piece of input.
00:20:06.919 I’d match a key with regular expression and if I got a match, I would fire off a proc. I wanted to specify the key value pairs in order because I wanted to say this regular expression has to precede this one. So, for me personally, there are valid use cases for it. I personally don’t like them and don’t use them, but we can all get along. However, they do make the interpreter more complex, and they do make it slower. Sometimes other people will use them, and I will occasionally look at someone else’s code and think: 'Oh, it’s Perl.' No, it’s Ruby! I personally advocate getting rid of them, but I’ll live just as long if we don’t.
00:21:39.919 So, what do you think is the right balance between too much syntax? Maybe it’s worth noting—some Ruby has a lot of different ways of expressing things. You mentioned syntax sugar, right? Punctuation means some people object to having such minimal syntax, but you have to spell everything out. I wonder if you think there’s a balance somewhere. I’m sure there is some kind of balance. There’s a balance between too much syntax or cuteness and too little. For one extreme, Lisp is full of parentheses per page, while Ada is full of semicolons and punctuation. APL was a bunch of things that nobody could type on their keyboard. It’s hard to say where the balance is and hard to articulate. One rule I have is that I don’t like too much punctuation, which is why I'm a proponent of what some people call poetry mode in Ruby.
00:23:57.579 I wouldn’t carry it as far as some people do. For example, when I define a method, I always use parentheses, but I don’t need to use parentheses when I call it. It’s better to have a language that is too expressive rather than not expressive enough, though some people complain about the fact that we have ‘for each’ and similar constructs in Ruby. It doesn’t bother me. Once in a while, I will use them, but it's kind of rare. If I've sketched out an algorithm that forces me to think mathematically, like when I took discrete structures, we used 'for' extensively. So I enjoy having different syntax alternatives, I hope that answers your question somewhat.
00:25:34.690 Anyone else? Yes, on your 'too many ways to identify' slide, you had blocks with the two block syntaxes. I was wondering if you object specifically to the existence of the two block syntaxes or the way people use them. I have pet peeves about how people misuse them sometimes, but I find both syntaxes useful in their right context. I don’t object to having two block syntaxes. I think it’s perfectly fine. I follow the convention that came from Dave Thomas: you put curly braces for single-line blocks and 'do end' for multiline blocks.
00:27:12.720 If I've used them nested, I want to avoid seeing right brace after right brace down a page; that’s just confusing. You know, first of all, you're nesting too much, and secondly, you’re just messing with my head. I appreciate you mentioning it, but I think it’s great that there are two syntaxes. Anything else? Yes, you mentioned before that the default values for arguments could give you the wrong answer. Really? I hadn’t noticed that! It’s likely that few people, if any, are using the arity feature, so it doesn’t surprise me that it’s broken for some common cases.
00:28:13.740 Someone else is discussing unspecified keyword parameters, and they just may be problematic in specific instances. It’s important to understand the distinction between calling a block versus yielding to it. Those have different semantics in terms of assigning parameters, and I can't remember how that works. I usually look it up, sometimes even in my own book, because all I did was buffer the information long enough to write it down; it doesn’t fit all in my head.
00:28:48.839 Seriously, 90% of the time, just do what’s common and simple and makes sense. Only venture out into more esoteric things when you have to. What's esoteric is a matter of opinion, as well. You mentioned a desire for unification of lambdas and procs and methods. In a way, I would like to see a unification, but I’m not really sure it’s possible because the differences are there for reasons. There are edge cases where you want them to work one way or the other, and they’re not always compatible.
00:29:45.200 I really wish that actual keyword arguments had shown up in Ruby earlier. I feel some of the effort placed into enabling default arguments for non-terminating arguments was misplaced. The ability to declare a function that takes a, b, and c and gives a default value to b seems unnecessary, as methods are going to be used to handle them properly. You could call one out of a million times you’d want to default the second of three arguments to a method, and if I ever do that, I would comment next to it, because it can be inherently confusing.
00:30:52.920 As for using a hash as a substitute for keyword arguments, I think that’s an ugly hack and should just go away. I mean, we could use a hash for all our arguments, and I am sure that someone—nope, over there—would argue against that. If you mean when showing all the symbols in a method, you will see various arguments, and you can easily find the source. You raised a good point, but I wish it could be simpler.
00:32:05.700 As far as looking things up, if it’s a method, it’s covered in RubyDoc.org. James Britt maintains that site, and despite some glitches, it’s very well done. I’ve spent hundreds of hours there, but for punctuation and symbols that aren't method names, just looking it up in the index is usually best since they’re usually sorted alphabetically.
00:32:29.640 The YAML parser allowing some nonstandard YAML syntax has caused many Ruby programs to not work correctly in other parsers. This issue has led the replacement parser known as Psych to be pretty much ignored because everyone’s used to the old one still working.
00:33:42.680 Before I forget, there’s one thing I haven’t mentioned: there have been times I wanted array and hash literals to call new. For example, if you say x equals bracket one, two, three, if you happen to override or alter array.new, it would be nice to have those things passed into it. Have any of you ever thought about wanting to do that?
00:34:54.160 You can. Array.new can take a block, but I once wanted to override hash.new and stick that feature in there, but it's not possible because when you create a hash literal, the 'new' method is not called. The interpreter circumvents that and gives you a hash. I might be the only one who wants to do that kind of thing. I admit, it's a little niche.
00:35:55.600 Okay, do you have any opinions on the interpreters coming out with JRuby and how you see that impacting what you’re discussing? I think MRI will always be the reference for all the others and sort of the de facto standard. At least I hope it is, especially since Matz is driving it, and he’s the genius behind all this.
00:38:06.760 I don’t see Rubinius, for example, deviating significantly from MRI's syntax and semantics in the foreseeable future. You might always find edge cases or behavioral issues where they differ; for example, one version might be ten times faster, or maybe in one version the threads won’t hang. But I hope they proceed in parallel, and I've been told I'm out of time. Thank you.
Explore all talks recorded at LoneStarRuby Conf 2011
+19