Apr 4, 2012

Ruby for the C# developer–The Basics

I have been reading 7 languages in 7 weeks recently and I decided to to start a series of posts that focusing on learning the languages discussed in the book from the point of view of a C# / .NET developer.

So let’s get started. First we are going to start with Ruby. This is the one language in the book that I already had some familiarity with. I have read Design Patterns in Ruby by Russ Olsen and The Well Grounded Rubyist by David A Black. Both are really good books that focus more on the Ruby the language and less on Ruby in the context of Rails.

So let’s talk about the basics of the Ruby language.

Type System

Ruby has a strong dynamic type system. This means a few things

  1. You will receive an error if types collide. (This is the “strong” part)
  2. You will get this error at runtime not at compile time (This is the “dynamic” part)

Let’s look at some code examples of this. First C#

int foo = 10 + "bar";

In C# this results in a compile time error:



Now the same thing in Ruby


foo = 10 + “bar”

Since Ruby does type checking at runtime this will not cause any errors until you try to call it. Then you will receive the following error (also notice the lack of type information to the left of foo):



Classes


Ruby is an object oriented language with a single inheritance model like C#.


Let’s define a class in each language. First C#


public class Foo
{
public int Bar { get; set; }
public Foo()
{
Bar = 10;
}
}

This is a simple C# class that defines one public read / write property called Bar and has a constructor that sets Bar = 10. And now the same thing in Ruby


class Foo
attr_accessor :bar

def initialize()
@bar = 10
end
end

This is the same as the C# class above. The initialize method is the constructor for this class. The attr_accessor keyword defines a read / write property called bar (as opposed to att_reader which is a read only property). In the initialize method you see we reference the bar variable with an @ sign. This is the syntax for referencing instance variables in Ruby.


Interfaces vs Modules


Both Ruby and C# are single inheritance languages. To propagate non-similar behaviors to classes C# uses Interfaces whereas Ruby uses “mixin’s” via the module keyword.


Let’s first look at an Interface in C#


public interface IBaz
{
void DoSomething();
}

Here we define an interface called IBaz that defines one method DoSomething(). If we want to make sure our Foo class implements this “behavior” in C# we make Foo implement IBaz. This ensures that our object satisfies the IBaz contract (meaning we can call DoSomething() on an instance of foo)


public class Foo: IBaz
{
public int Bar { get; set; }
public Foo()
{
Bar = 10;
}

public void DoSomething()
{
//elided
}
}

Now lets look at how this is done in Ruby via “mixin’s”. First lets define our “mixin”


module IBaz
def do_something
#elided
end
end

Notice the subtle difference here? In the C# version we were simply defining a contract which our class must implement itself. In Ruby the implementation actually lives in the module (I named the module IBaz to keep with the C# example but that isn’t a Ruby naming convention).


And then to make sure our Ruby class can call the IBaz DoSomething we need to “include” it like this:


class Foo
include IBaz
attr_accessor :bar

def initialize()
@bar = 10
end
end

There is a key fundamental concept happening here. Modules can implement behaviors directly in Ruby. This means that a given type doesn’t implement “included” behaviors natively.


Duck Typing and Mutable Types


This is simply a fundamental difference between C# and Ruby. Many thanks to Arne for working through these thoughts with me.


Types in Ruby are mutable, even at runtime. This means that method dispatch cannot be resolved at compile time. This means that method dispatch is resolved at call time.


Call time method dispatch might sound similar to how the dynamic keyword works in C#. However, in Ruby there is a difference to be aware of.


The “method call” is simply a message that is sent to a given type instance. That message is then sent up the type hierarchy to see if there is any thing that can handle the message. This process involves looking at native methods on the type, “included” methods from mixins and checking the method_missing method.


In C# by using the dynamic type you can get past compile time checking of method call sites. However, at runtime if the object doesn’t contain the called method in it’s inheritance hierarchy the call will fail. In Ruby there is a language construct called method_missing that allows you to hook an unknown method call and do something with it.


This is where we will pick up in the next post. Open classes and the method_missing method


This is part of a larger series of posts inspired by the 7 languages in 7 weeks book. Use the 7LangIn7Weeks tag to find the other related posts

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home