Beware First Impressions
Saturday, 1 July 2006
Posted by austin in: Ruby, trackback
Adam Connor doesn’t like a few things about Ruby. He’s certainly correct that there are warts in the Ruby language, but much of what he’s described aren’t warts.
There’s more than one way to do it. If you like baroque languages like Perl, maybe this is a feature. Ruby has 3 ways of constructing regexps, innumerable ways of producing strings, if and unless and… well. You get the idea. Reading Ruby is an exciting adventure in learning new ways to do the same old thing! Don’t like the old method name? Alias it! After all, languages should enable free expression in whatever manner best suits the developer’s personal style. “`Twas brillig, and the slithy toves…” and all that. Moderation is a virtue.
This is a conflation of several issues. Let’s tackle them independently.
- Multiple constructors for regular expressions: I am presuming here that Adam is referring to
/re/,%r{re}, andRegexp.new("re"). Each one of these makes sense, but you only see two of them commonly present in most Ruby code. Most often, you’ll see the/re/format, like/[ab].[d-f]/. This works well until you need to match on a slash character, like/\d{4}\/\d{2}\/\d{2}/. The extended format is much more readable in this case:%r{\d{4}/\d{2}/\d{2}}. Note that Ruby doesn’t have problems with the braces because they match here. Ruby is smart that way. The third form exists because regular expressions — like everything else — are objects. One could construct a regular expression from a string you get externally in this case, likestr = "[ab].[d-f]"; Regexp.new(str). - Multiple constructors for strings: Like regular expressions, there are good reasons for this, mostly related to quote escaping. With Ruby’s string interpolation (
"foo#{bar}") it becomes useful to have a form of the string that does not interpolate:'foo#{bar}'. (It is a minor quibble that there is no clean way to later take that string and interpolate it without calling Kernel#eval.) This means that it is useful to have string forms that make it easy to have quotes:%Q{"foo#{bar}"}and%q{'foo#{bar}'}. Finally, there is the HEREDOC format, which allows multiline strings without having to have line continuations or C++’s autojoin functionality (which wouldn’t work in Ruby because each line is an expression to be evaluated). - Multiple method names for the same method: The biggest example of this is Enumerable#map and Enumerable#collect. These two methods do the same thing, but they are named such because Lisp-oriented developers understand #map implicitly; Smalltalk-oriented developers understand #collect. When I started with Ruby, I started with #collect; now I use #map. Other examples include Array#indexes and Array#indices. Grammatically speaking, #indices is correct, but #indexes is common, too. (The same applies to if/unless, although I only use unless as a modifier with a single test, most of the time.)
Perl-style two-character variables. Because dollar-sign-squiggle is such a mnemonic and meaningful name, and distinguishing $` from $’ is just a snap.
These are not used often; it is more common to see $LOAD_PATH instead of $: these days.
Other esoterica. The flip-flop operator [...]
This has been deprecated and I’ve never used it in any of my code.
Whitespace is significant. Didn’t this go out with JCL and punch cards? I guess Python has made it fashionable again. Anyhow, I can’t tell you how thrilled I am that
1 + 2 +<br /> 3
evaluates one way and
1 + 2<br /> + 3
evaluates another way.
The problem here is not Ruby. Ruby evaluates each line as an expression. This doesn’t mean that whitespace is significant in Ruby (it isn’t), but that the two examples given are two different expressions. What is a bug, and Matz has said will be fixed is a subtly different problem, is:
(1 + 2 + 3)
The parentheses should be sufficient indicator of an expression.
Constants aren’t. You can assign to a constant multiple times. At least it will generate a warning.
Variable/Method Ambiguity. That’s what the pickaxe book calls it, anyway… Ruby guesses whether a name is a variable or a function based on whether it has seen an assignment to that symbol before the point of call. I guess I’m naive; I thought that was so amazingly lame that I was surprised it wasn’t classified as a bug.
Both of these are fully understandable and well-explained elsewhere. The latter is a consequence of what is often called “poetry mode” where parentheses aren’t required for method calls. Ruby does resolve this very well.
The method to_i doesn’t return errors.
Use Kernel#Integer if you need this. Most of the time, I don’t.
Handling namespaces and mixins with modules. Since these concepts have no particular overlap, this just feels very kludgey to me. Apparently the Ruby community is OK with it, though.
Actually, it’s not just modules. It’s classes. I can do either:
class A; end class A::B; end module C; end class C::D; end
This, to me, is a lot more sane than yet another syntactical setup with no structure (package) or the way that C++ uses namespace.
Ultimately, I think that Ruby is what Lisp would be if it were a procedurally-oriented language. Nothing else certainly comes close.




Comments
Sorry, comments are closed for this entry