Let me make this disclaimer first before anything else - I'm not jumping off the .NET boat or anything. But I definitely see value in opening up and seeing how people do things on the other side of the fence.
So I've been reading this book online - Programming Ruby - The Pragmatic Programmer's Guide. In the chapter on Standard Types, they showed this really short code sample. Even though it's short in lines, it displays a bunch of things about Ruby that I think are really cool. And things that are awesome in comparison to the way .NET handles things.
Here is the code and the output in Ruby:
num = 9
7.times do
print num.class, " ", num, "\n"
num *= num
end
# OUTPUT
# Fixnum 9
# Fixnum 81
# Fixnum 6561
# Fixnum 43046721
# Fixnum 1853020188851841
# Bignum 3433683820292512484657849089281
# Bignum 11790184577738583171520872861412518665678211592275841109096961
Here is the code to do the same thing in C# and its output:
private static void WithoutExtensionMethods()
{
int num = 9;
for (int i = 0; i < 7; i++)
{
Console.WriteLine("{0} {1}", num.GetType(), num);
num = num * num;
}
Console.ReadLine();
}
// OUTPUT
// System.Int32 9
// System.Int32 81
// System.Int32 6561
// System.Int32 43046721
// System.Int32 -501334399
// System.Int32 2038349057
// System.Int32 -1970898431
Now, to be a completist, I also wrote a version that actually allows you to do something like 7.Times() in C# - it used an extension method on the int type and you had to pass the number you wanted to multiply (the 'num' variable) as well as a Func<int, int> telling it what exactly you wanted to do inside the loop. I didn't show that example because I figured it was extra work and this is probably more likely how someone would do the code in the Ruby example in C#. Ruby does have a for loop construct, but most Rubyists prefer using times, or upto or other methods as they're more clear to the programmer.
I'm going to cover the things that I think make the Ruby example cooler than the C# from the lighter "not that big a deal" stuff to the stuff that really IS that big a deal. So without further ado.
1. The Ruby code reads better.
That's pretty subjective, but in my opinion it does. Your brain doesn't have to parse the for loop and figure out "ok, its starting at zero and running to less than 7 (meaning 6) so its going to run 0-6, so 7 times". The loop construct in Ruby is "run this 7.times". Another cool thing I found out about Ruby is that you can actually initialize big values using an underscore to break them up. So "123_456_789" would just become "123456789" to the processor. But its easier for another programmer to read that value with the underscores - this is the same thing that commas do in math - allow our brains to process the number easier. Same thing that dashes do in phone numbers, etc. The phone company doesn't need us to enter dashes in... it just needs 10 numbers to know what to call. The dashes are for our brains. And that's a lot of what programming is about. The computer understands complexity. You're not going to over-complex the computer. So we write stuff cleanly and simply so that other programmers can read and understand your code.
2. The Ruby code block passed into the times function is actually aware of the context in which its running.
Again, I didn't show the C# version of the Times() function that you could write, but you do have to pass in the num variable so that the Func<int, int> knows what to work with. Not the case with Ruby. To further illustrate this point, scroll down to the LAST example at the BOTTOM of this page of the Ruby book I mentioned earlier.
With Ruby, that part between do and end is a block of code, similar to a function pointer. It doesn't run immediately when its called, but rather waits until its called, just like Func<> or a delegate. Only, with Ruby, it remembers the context when it was called - the local variables, the current object, and so on - so its able to work with those values inside the block.
3. Ruby is dynamic.
The first listing in the output for both types is the class or type of the variable. Notice how in the Ruby example, when the value gets large enough, the variable changes to type Bignum instead of Fixnum and keeps on chugging. In C#, that's not the case. If you defined it as an Int32, that's what it's going to stay. And that's deceptive and brings me to my last point.
4. Ruby gives you the right answer.
Because its dynamic, Ruby can compute values this big and store them in the correct type on the fly.
I'll be honest... when I coded this example in C#, I was expecting an OverflowException to be thrown once the number got too big. The actual results scared me a little. It runs just fine, but the results are incorrect. In the 5th line, it's gone into negative numbers as we've overflowed the number of bytes that an Int32 can hold (for the record, I ran this and used an explicitly type Int64 and it overflowed as well).
I tried running this again and wrapped the computation in a try-catch block to catch the OverflowException explicitly. It still ran just fine. You have wrap that try-catch block in ANOTHER block called the checked block to get your program to check the exception. And then your program will crash and die when you overflow the Int32 type. That's good and bad. It lets you know you've used a type that isn't big enough for the value you're computing... but it also doesn't get you the right answer or allow your program to continue to running .
Of course there are tradeoffs. Because just like you had this num variable change from a Fixnum to a Bignum, you could set it to a String and it'll just chug along and not complain. It's up to you to watch that stuff. You could argue whether that's good or bad (I tend to think that it's not that bad... you oughta know your code well enough to know what its doing). But either way... it's a different perspective. Static languages aren't the only way of the world.