Early Observations on Ruby

I’ve been playing with Ruby for a few weeks now, and I have really enjoyed it so far. On the one hand, it’s a bit scary not to have the rich type checking and static analysis I’m used to from writing C# in Visual Studio. On the other hand, my source code feels a bit more concise, and I can’t paint myself into a corner with poor decisions about classes and interfaces. (I’m sure I can paint myself into a corner in other ways; I just haven’t found them yet.)

I am reading Sandi Metz’s Practical Object Oriented Design in Ruby, and I have really been enjoying it. Writing for an audience assumed to be more familiar with Ruby than with Java or C#, she stops to explain static type checking as a type of unit test:

You familiarity with Ruby may lead you to take this transition [from depending on concrete objects to depending on abstractions] for granted, but consider for a moment what would have been required to accomplish this trick in a statically typed language. Because statically typed languages have compilers that act like unit tests for types, you would not be able to inject just any random object into Gear.

(Practical Object-Oritent Design in Ruby, page 54).

I thought that this was an interesting remark. On the one hand, type safety is comforting, and, as Metz points out, makes explicit when code depends on an abstraction like an interface, and when it depends on a concrete object; on the other hand, baby-sitting a system for generating tests that type-check the return values and parameters of all your methods, all the time, even when you don’t want them to, can be tedious. I see arguments on both sides.

Anyway, I’ve officially switched jobs from writing C# all day to writing Ruby all day, so I wanted to share a few early lessons for coming to Ruby from C#.

Select Doesn’t Mean what you Think It Means

In C#, IEnumerable<T>.Select() does the job that in many langauges is done by a function called “map”: It takes the collection, and performs and item-wise transformation of the collection, giving a new collection. I think C# called this method Select() becauase they wanted us to be able to write SQL-looking LINQ statements. Anyway, select is a real method on collections in Ruby, but it takes a boolean predicate and filters the collection. It works fine, but it did surprise me the first time I called it.

Symbols Are a Thing

Ruby has a primitive type that I just haven’t seen in other language: the type symbol. Basically, a symbol gives the programmer the ability to refer to a symbol in the symbol-table that Ruby scopes use, I believe. I think that its closest cousing in C# would be something like const string foo = "foo";. When I first started using Ruby, I used hashes everywhere, but I was using string literals for the hash keys. The preferred practice, assuming you control the code that produces and consumes the hash, is to use symbols if you know ahead of time what the keys will be. I found a helpful StackOverflow question explaining that the reason is that the runtime can cache the hashcode of symbols, and never has to re-compute them, so lookups into hashes whose keys are symbols are faster, and Ruby doesn’t have to allocate string objects. So, use symbols in hash keys.

Attribute Readers are Like Properties

In C#, it’s pretty common to have a field that’s meant to be accessed only via a property. In Ruby, the same thing is done, but it works a little differently. Consider these samples:

class A 
    private string _foo; 
    public string Foo 
            return _foo; 
            _foo = value; 

And in Ruby

class A
  attr_accessor :foo
  def initialize
    @foo = 'whatever' 

These are fairly equivalent. Basically, the attr_accessor directs Ruby to automatically generate getter and setter methods for the symbols that follow. You can also use attr_reader to make a getter but not a setter, and you can use attr_writer to make a setter but not a getter. (I’ve never made a write-only property on a class before, but hey, you never know.)

Sandi Metz explains that it’s important to use these automatically generated getters and setters because they keep the rest of your code from knowing about the backing field, which allows to change the internals of the class more easily without breaking things that depend on it. This is basically the same reason we use getters and setters in C#, but the syntax is different enough that I wanted to mention it here.

Anyway, learning Ruby is a new adventure for me, and I’m pretty excited; I hope you’ll join me for some future posts about the things I learn.

Till next time, happy learning!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s