Perl vs Ruby

25 Mar 2011

This isn’t like Perl vs Ruby: FIGHT! This is more like walking vs running: COMPARE!. They will both get you there, just in different times and sweat amounts. :)

I stopped doing Perl in about 2002 but not because I hated Perl. I stopped doing Perl because started learning Java. Java was where the jobs were. Since all that, I know I’ve become a better developer so a lot of this is situational to me and not a reflection on the language. Without a control group, you can’t say that the language has made me any different. I’ve learned things (not all things) from each language, environment and community as I’ve bounced around. No doubt, I’m Ruby-biased so please post comments, corrections and better ways of doing things in Perl so that I’m the most accurate I can be. Recently it’s popped back up at work and I needed a refresher but

Ok, enough about that. I’m here to compare how you do things in each language. Ruby is all about blocks, syntax sugar and a very different community than Perl. Both share many idealistic values (such as there’s more than one way to do something) and Ruby was inspired a lot by Perl. So this isn’t a battle but more of a Rosetta stone.

Methods/Functions

Notice that we have to print a newline. In perl, there’s no println or puts. In Perl 6, there will be a say function which will do a print with a newline. But Perl 6 is not out.

A neat trick is the $_ variable which means “an array of arguments”. You can’t do this in Ruby so you have to explicitly define arguments otherwise you raise an ArgumentError. $_ is a little bit line-noisy but that’s the way I remember Perl.

defadd(a,b)a+bendputsadd(1,2)# => 3

So both of these functions do the same thing (although horrible and useless). Both can’t take three numbers: add(1,2,3). In Ruby, you get a run-time ArgumentError exception. In Perl, the third number “3” is thrown away and it still adds 1 + 2. Of course, this isn’t how you would do this in Ruby. You would use the splat operator and add could take any number of arguments. You can do the same thing in Perl but it’s not automatic or nearly as clean.

Now now our Perl function can take any numbers and add them up. This is how we’d do this in Ruby.

# thank you PJK in commentsdefadd(*arguments)arguments.inject(&:+)endputsadd(1,2,3)putsadd(1,2,3,4)

We can even just pass one number in and it will work in both languages. I think both are fairly readable except for the fact that Perl is starting to look line-noisy with the @_ and $ all over the place. The advantage here is that you know what kind of object it is by looking at the code. The disadvantage is, Ruby doesn’t care because of duck-typing.

Method weirdness

One thing I forgot or never knew is how Perl does it’s lifecycle. As I’m writing this, I’m redefining functions (methods) and they aren’t doing what they are supposed to. For example, I would expect this to print muffin:
$ perl -e 'sub food { print "muffin\n"; } food;'

And it does. But I would expect this to print muffin and then asphault:
perl -e 'sub food { print "muffin\n"; } food; sub food { print "asphault\n"} food;'

But it prints asphault twice.

You can get around this by using Sub::Override but omg this is horrifying. I have to download a CPAN module, install it (which is easy) and then my code looks like this:
perl -e 'use Sub::Override; sub food { print "muffin\n"; } food; my $override = Sub::Override->new( food => sub { print "asphault\n"}); food;'

This is not how Ruby works and I think that’s bad because it makes more sense in Ruby. If I redefine food(), then it should be redefined at runtime. Ruby version:
ruby -e 'def food() puts :muffin end; food; def food() puts :asphault end; food;'

Note that writing ruby one-liners is messy as crap. This is also horrifying! I can’t make it much better by using semi-colons:
ruby -e 'def food;puts "muffin";end; food; def food;puts "asphault";end; food;'

Perl one-liners are nicer. Ruby method redefinition in a normal file/program is nicer.

Environment and Libraries

Gems and perl modules are almost the same thing. I like Ruby’s gem command for interactive installation more than the CPAN shell but both are about the same if you install from a tarball. However, ruby gems don’t need to be extracted. Perl has a lot more modules but I never got far enough with Perl to learn what killer libraries are out there. Ruby toolbox has what I think is a killer list of libraries that get really high-level functionality done quick. For example, the savon library for webservices does quite a lot of work in just a few lines of your code.

Perl modules have namespaces which is a O_o moment for me in Ruby. I’ve never run into a namespace collision but if you’ve ever taken a look at what is actually in scope, I don’t think this is going to scale forever. I guess as long as people don’t all use the same CONFIG symbols and stick to modules as namespaces, everything will be ok. I still would prefer an option to import library.module or Library::Module or something like that.

Deployment

Massive generalizations incoming. I can’t cover every edge case. So I’ll just say that Perl is installed everywhere. Ruby is installed someplaces. On Windows, neither is there and both are a pain but not because of the interpreter itself but because you need a real dev environment (gcc etc). DevKit in Windows might solve both problems. I’ve followed EngineYard’s lead and gotten Ruby on Windows up and running using their installer and DevKit. But I haven’t done it multiple times because it’s a pretty new initiative by them.

So the Perl that ships with the OS is probably ok. The new hotness right now seems to be some version of 5.10 which 10.10 comes with. Ubuntu at least gives you 5.10 if you do an apt-get but Ruby is completely unusable. If you’re not a rubyist, 1.9.1 is taboo. It doesn’t run rails 3 and 1.9.2 is pretty much the current version (although a little bleeding edge). I use it as my main now and haven’t had any major gotchas. On mac, Perl is probably broken. On mac, Ruby is definitely broken, it ships with 1.8.6 as of 10.6.6 and that’s completely old and busted. So in either case, you probably have some work to do but Ubuntu/Linux gives Perl the edge.

RVM is absolutely awesome, it allows you to switch interpreters at will, create/switch collections of gems (modules) and installs the interpreter in your home directory so that you don’t have to sudo everything. If your home directory floats with you on a network, then it’s even more awesome because your ~/.rvm directory has your entire dev environment exported over NFS or whatever. But RVM is not the default way of installing ruby, it is not integrated into your OS package manager and it does not run on Windows. So you have to discount RVM.

Powah

Ruby can do more in one line than Perl can do. Who cares? Well it speeds up development time and reduces bugs, at least in theory. I know there’s way more to development than syntax but there isn’t a whole lot more to putting a smile on a developer’s face when he thinks about how hard a problem could have been.

Ok, so let’s take a bunch of strings and capitalize them.

# capitalize all in array@list=qw(one two red blue);print"Capitalize all in array: ";foreach$item(@list){printuc$item." ";}print"\n";

This gives us: Capitalize only colors in array: RED, BLUE. Not great. Lots of code, lots of weird symbols that won’t make sense to you in a year (imo). What is @seen{@list} = (); all about? Even if you explain it, why do I have to do all that for what Ruby does with include?(). We’ll get to that in a second.

First, let’s capitalize (actually uppercase) all words in an array in Ruby:

# capitalize all in arraylist=%w(one two red blue)capital_colors=list.collect{|item|item.upcase}putscapital_colors.join(", ")

Ok 4 vs 7 lines. And we can shorten it too 3 lines.

# capitalize all in array, shorterlist=%w(one two red blue)putslist.collect{|item|item.upcase}.join(", ")

Shorter still:

# capitalize all in array, even shorterlist=%w(one two red blue)putslist.map(&:upcase).join(", ")

Ok, what about only capitalizing color words then? How much different does it look than Perl?

So is this more readable? Not really. The & method of an Array in Ruby is pretty terse and cryptic just like Perl’s shortcuts. But it is documented in the stdlib. But if we want to make it a little more readable then we can do this:

How ugly is it? How long is it? Which is better? Well even the longer Ruby form of capitalizing colors is 7 lines including the comment whereas the Perl version is 8. Well who cares? The Ruby version is way more readable in either form. I can add comments like “for each item in the list that intersects, capitalize and join with commas”. With the Perl version, I would have to explain what the temp variable is. Perhaps this is a comfort opinion as a Perl person would wonder what the heck &:upcase means. In any event, Ruby has methods that are at least named something meaningful and you can chain them along like Python. However, unlike Python, Ruby has blocks. And blocks are extremely powerful which is also why the Perl person wouldn’t understand why &:upcase is so awesome.

Perl really only has one way to go through an Array. You loop through it. Ruby has that but more. You can loop plainly with .each() but you can also pass a block in to do something. You can even return a new Array with .collect() or .map(). But really it’s not about looping over the Array that is the important part. The distinguishing feature (again) in Ruby is blocks. It’s the fact that you can pass a block to .each() and .collect() and avoid having to iterate over and over again. It’s the fact that a block can be passed to a file iterator, a database result set, a hash and an array and they all acts the same. For a really good presentation, you should check out Gary Bernhardt’s presentation he did about Ruby to a room full of Python people. Also, check out his Play-by-Play at Peepcode.

In any event, this contrived example is just the beginning. Ruby gives you crazy powah and not just iteration.

REPL

Ruby has a shell called IRB that gives you REPL hotness. Perl can do an eval loop too like this:
# put into .bashrc
alias 'perl-repl'='perl -MData::Dumper -MTerm::ReadLine -e '\''$r = Term::ReadLine->new(1);while(defined($_ = $r->readline("code: "))){$ret=Dumper(eval($_));$err=$@;if($err ne ""){print $err;}else{print $ret;}}'\'''
But is that good? Irb comes with Ruby. It’s not only part of it but there are gems that extend it. Irb has autocompletion (like how you hammer the tab key in bash), colors and introspection tricks like the following. Let’s say I have a new library (gem) that I’m not familiar with. I fire up irb:
$ irb

I have no idea what Plucky is or what it does. So I ran public_methods to see what it does. The list of public_methods goes off the screen to the right. There’s a ton of them because it’s inheriting from the class Object and who knows what else. But following a trick found on stackoverflow, I can monkey-patch the Object class to only show what the difference is between it and Object (ruby has single inheritance so there’s no chain). So I put this in my .irbrc:

classObject# Return only the methods not present on basic objectsdefinteresting_methodsifself.is_a?Class(self.public_methods-Object.public_methods).sortelseifself.is_a?Module(self.public_methods-Module.public_methods).sortelse(self.public_methods-Object.new.public_methods).sortendendendend

Ok, so there’s 1 method that is possibly interesting. But what is this Plucky object? If I call .class on it, I find out that it’s a Module which is being handled in the .irbrc snippet above. Well, a module usually doesn’t have much use by itself. Usually a module includes classes that actually does stuff. So let’s try to find a class by looking at Plucky’s constants:
Plucky.constants
[:Extensions, :CriteriaHash, :OptionsHash, :Query, :Version, :Pagination]

Wirble::Shortcuts and PP::ObjectMixin don’t count because we’re in irb and those are irb mixins specific to “pretty print” and wirble which just make irb more pretty. So Enumerable and Kernel are the modules that Query uses. Enumerable most likely contains each. Let’s write a quick script to find out.

# what methods does Enumerable give you?classFoo;endbase_methods=Foo.new.public_methodsclassFoo;includeEnumerable;endenumerable_methods=Foo.new.public_methodsspecial_methods=enumerable_methods-base_methods# monkey patch for interesting_methods outside of IRBclassObject# Return only the methods not present on basic objectsdefinteresting_methodsifself.is_a?Class(self.methods-Object.methods).sortelseifself.is_a?Module(self.methods-Module.methods).sortelse(self.methods-Object.new.methods).sortendendendendrequire'plucky'plucky_methods=Plucky::Query.new("muffins").interesting_methodsclassFoo;includeKernel;endkernel_methods=Foo.new.methods# gets close but no cigarputs(plucky_methods-special_methods-kernel_methods).join(",")

Unfortunately, this isn’t quite right according to ri Plucky::Query. But at least we have a clue as to what this thing is like. If we hit up his tests directory in the source, we can see we were getting close.

The point of this extremely long section is all about REPL. Here we are playing around with an API directly with no knowledge of it because we have IRB. We at least learned that it has something to do with an iterator, fields and finding things. That’s pretty close to what is shown in Nunemaker’s tests.

In Perl, we don’t have a REPL loop that’s as good. We can do a simple eval shell but it’s not as polished nor as often used.

IO Speed

In simple cases like “find me all the things in this file that look like phon numbers”, Perl consistently beats the pants off of Ruby. Maybe because Ruby has to allocate objects to do anything (like regex work). Perl’s I/O is very fast. But then we end up with a mess of Perl code that is procedural. So for simple apps, I/O is great but then all you end up with is a bunch of small Perl scripts. How can you build a large Perl application?

So for sed and awk style one-liners, sometimes I will reach for Perl. But when it becomes complex, I rewrite it in Ruby. You don’t have to learn Moose to get something other than procedural style.

Conclusion

I can’t really give one. There’s no final argument, no silver bullet. There are many things that I have gotten used to in Ruby and switching to Perl, even for this post is very difficult. I’m faster in Ruby now than I was in Perl. I’m fairly sure that I couldn’t be as fast or elegant in Perl. But it’s not just the language, it’s the REPL experience. It’s “gem list” vs CPAN. It’s the community of test-driven-development and a whole bunch of other soft features. None of it is a deal breaker and Perl isn’t failing to solve real problems everyday. Perl is portable, well established, installed by default and it excels at text manipulation. But I would say, despite all the good of Perl, Ruby sucks less.