RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Ruby Comes to the .NET Platform : Page 3

Find out why .NET programmers may want to learn and use Ruby, and discover the core syntax of the language.

Blocks and Closures
Although there were things like event handlers in .NET 1.x, you were forced to define full methods to attach to events when you wanted them handled. This led to the creation of a lot of methods that really only existed because the framework demanded it.

.NET 2.0 introduced the concept of anonymous delegates. They function in much the same way as blocks do in Ruby:

   irb(main):001:0> h = {:foo=>'bar', :hi=>'there'}
   => {:foo=>"bar", :hi=>"there"}
   irb(main):002:0> h.each_key {|k| puts k}
   => {:foo=>"bar", :hi=>"there"}
   irb(main):003:0> h.each {|k,v| puts "#{k}: #{v}"}
   foo: bar
   hi: there
   => {:foo=>"bar", :hi=>"there"}
As you can see, the syntax for blocks in Ruby is quite terse: Use curly braces to open and close the block, and use the |x,y| syntax to designate the variables passed to the block.

Blocks in Ruby also act like closures, just as anonymous delegates do in .NET 2.0. This means they have access to values in their enclosing scope, even after that scope has exited. Here's a simple closure that multiplies together several values:

   irb(main):004:0> n = [5, 6, 10]
   => [5, 6, 10]
   irb(main):005:0> t = 1
   => 1
   irb(main):006:0> n.each { |i| t *= i }
   => [5, 6, 10]
   irb(main):007:0> t
   => 300
You can even store references to blocks to be used later:

   irb(main):008:0> t = 1
   => 1
   irb(main):009:0> f = lambda { |i| t *= i }
   => #<Proc:0x0818a7b0@(irb):9>
   irb(main):010:0> n.each &f
   => [5, 6, 10]
   irb(main):011:0> t
   => 300
Method definitions in Ruby are simpler than in .NET, because types are not specified:

   irb(main):001:0> def greet(name)
   irb(main):002:1>   puts "Hello, #{name}!"
   irb(main):003:1> end
   => nil
   irb(main):004:0> greet "Reader"
   Hello, Reader!
   => nil
   irb(main):005:0> greet 42
   Hello, 42!
   => nil
Ruby performs something called "Duck Typing": if it walks like a duck and quacks like a duck, it must be a duck. You don't ask it "Are you a duck?"—you just assume it's a duck and call quack on it. Even if you were expecting a duck, anything that can quack is allowed to join in the party.

Note that the greet method definition didn't set any limits on the types of objects that can be passed in, because it just prints them—and everything in Ruby supports printing, by virtue of the to_s method on Object, which will convert the object into a string. The end result is that you can greet anything.

Here's another example:

   irb(main):006:0> def print_len(item)
   irb(main):007:1>   puts "Len = #{item.length}"
   irb(main):008:1> end
   => nil
   irb(main):009:0> print_len "Reader"
   Len = 6
   => nil
   irb(main):010:0> print_len [1, 4, 9]
   Len = 3
   => nil
   irb(main):011:0> print_len 42
   NoMethodError: undefined method <span class="pf">'</span>length' for
           from (irb):7:in <span class="pf">'</span>print_len'
           from (irb):11
The print_len method now does more than before: it calls the length method on the object passed to it. So the requirement for something to be passed to print_len is that it has a length method that you can call with zero parameters. Passing a string or an array works because they have an appropriate length method; but passing a number doesn't work, because they don't have a length method.

To write a method like this in .NET, you'd probably need to invent an IHaveLength interface and use that as your parameter type. For classes invented before you created your interface, you're out of luck, or you end up creating type converters. The whole thing spirals out of control quickly as the strong typing system gets in your way.

On the other hand, at least if you had IHaveLength, you understand what the requirements of the method are. Since types act as a form of documentation, you need an alternative in dynamic languages, which means you will be more reliant on things like written documentation and unit tests to help discern how some class or method is meant to be used.

Ruby supports default argument values:

   irb(main):012:0> def repeat(val, times = 5)
   irb(main):013:1>   val.to_s * times
   irb(main):014:1> end
   => nil
   irb(main):015:0> repeat "hi"
   => "hihihihihi"
   irb(main):016:0> repeat "hi", 3
   => "hihihi"
   irb(main):017:0> repeat 10, 3
   => "101010"
Notice that there's no return before val.to_s * times. Methods in Ruby always return the result of the last evaluation, unless explicitly told to return a value, so the return keyword is just seven extra characters you don't need to type.

Ruby supports variable arguments:

   irb(main):018:0> def add(*values)
   irb(main):019:1>   result = 0
   irb(main):020:1>   values.each {|x| result += x}
   irb(main):021:1>   result
   irb(main):022:1> end
   => nil
   irb(main):023:0> add 1, 2, 3, 4, 5
   => 15
Ruby packages up the variable arguments into an array. You can then access the values that are passed and (in this case) add them together.

Method and Variable Naming Conventions
Methods in Ruby start with a lowercase letter, and can contain letters, numbers, and the underscore.

Method names that change the underlying object end with an exclamation point (!). For example, the upcase method on strings returns the uppercase version of the string, but leaves the original string alone. In contrast the upcase! method actually changes the underlying string.

Methods that answer questions (returning Boolean values) have names ending in a question mark (?).

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date