I Can't Change Your Mind. And I Don't Want To
... and I said as much yesterday on my Tumblog. There's no point in my trying to convince you that LanguageX is better than Y - and I'm not going to. Languages are a tool - if you pick sides and go with only one then you're being ridiculous . However I know a lot of people are curious, and they find by understand what *I* like, it helps them to form their own opinion. I think that's a bit co-dependent, but I can also see the merits of it.
I like the way Miguel put it to me today on Twitter:
A better C#. Let's start right there.
C#, Microsoft's Leatherman Language
I like the whole Razor/WebMatrix idea, believe it or not (read: the *idea*, not what was pushed out). I was wandering the halls at MS when the idea was being tossed around - but all I ever heard were small mentions and so on - I was never looped into the discussions. I think it's generally understood that Microsoft sees PHP's ability to get people to up speed quickly as a great "vector" for platform adoption. There are a ton of resources out there on PHP, and the language is fairly simple (at first). The tooling, also, is rather simplistic (Notepad).
That seems to be where the WebMatrix team stopped: "Let's make a tool that looks and acts like a scripting experience and we'll make it free!" Great idea! But then... "we'll leverage ASP.NET and C# - building out an additional abstraction layer that we'll say is 'concept-light'". I'm not alone in being confused (Tweet from David Hayden):
If WebForms and MVC were more productive, intuitive, and convention-based out of the box, would we need WebMatrix?
The thing that struck me sideways is that it's sort of like WebForms 2.0 - you have the same stack underneath it (C#, ASP.NET, and .NET in general) but now you have "Scripter Components", which are modeled after PHP's snarled mess of core functions. The concept is interesting, but masking the language and framework - essentially dressing it up like a college kid - seems like sleight of hand.
A bit of a buildup - but here's the core point:
It's clear that C# isn't the best language to use for Web Development. Just ask the guys who are writing the frameworks with it.
ASP.NET is built on abstraction - "backing [the framework] baby into the corner". Companies have made millions on components designed to seal away and "black box" web functionality. And now we have Razor, which all but buries C# under a funky DSL that, all in all, is pretty functional.
It's just confusing to the existing community. Which is OK - it's not designed for them. It's designed for people who need to get in and out fast - the people who fall backwards into becoming web developers because they have to support their company/department intranet (or blog, or commerce site... whatever) which uses Joomla or Drupal (or Wordpress). These people just need to know enough Razor to create plugins for these platforms - and if they're naturally interested in the process... well a web developer is born.
So it makes sense - except for the whole PHP part. Maybe when DotNetNuke switches over to Razor... But this post isn't about Razor... let's get back on track...
C# is an atomic powerhouse of a language - the power of the .NET framework clearly shows that. The web, however, is a pretty simple system that really doesn't need all the jargon and patterns we throw at it from day to day.
In response to Miguel the only thing I can say is:
C# is just fine. I just don't like building websites with it all that much.
Couldn't they have used javascript for Razor? Or Ruby... or Python? Or *any language* that's in the DLR? Who was serving the punch at these design meetings?
Why I Like Ruby
Geoffrey Grosenbach was kind enough to drop me a line about some better ways of handling Ruby. Specifically - while mocking is possible with Ruby (below) - it's not reliable. Please read the example as an ability with Ruby - not the way to accomplish mocking.
Geoff also mentioned that the method_missing way of building out a query is rather expensive perf-wise. Ruby has to scan all methods before falling back to method_missing. It's still a good example RE what Ruby is capable of - but it's not something you should do every day.
Before I jump into this - you're going to think "Oh Rob's left the MS stack... whatever this is ranting". You're free to think what you like - but I haven't. I need to bring this up every time I talk about something that's not mainstream MS - which is actually a bit sad. Ruby is supported by the DLR; many people (including myself) would love to see it become a bit more mainstream.
I really love this article from Ben Griswold (aka JohnnyCoder) who really tries to capture the essence of working with Ruby. You've heard it before, Ruby is a very malleable, forgiving and fun language that is a treat to work with.
I'm going to show you some code now - and I'd really appreciate it if you could not get defensive (if you're a C# person). Recognize that I am too - and also recognize the context: Web Development. So put your geek hat on and take a trip with me...
Case 1: Expressiveness
You've heard it - but what does it mean? Let's consider a few examples - the first is handling a NULL value. In C# it would be something like this:
if(myVariable != null){
//handle it
}
Rubyists hate this "machine-like" way of thinking about variables and their "assignments". They think in terms of interrogation, rather than process-flow, so they would write the above like this:
unless(my_variable.nil?) #do whatever end
Something to consider about the Ruby syntax above is just how readable it is - right down to the question mark. For some developers this is silliness - what amounts to putting stickers on your computer. For others - it's instantly readable and the microsecond you didn't have to go back and read over the machine syntax of C# you can put to use reading the rest of the code.
Let's jump to another example: exceptions. In C# the normal flow might be something like this:
if(myVariable != null){
//handle it
}else{
throw new InvalidOperationException("Hey dude! Where's My Variable Assignment!")
}
With Ruby you can do this slightly cleaner:
raise "Hey dude! Where's My Variable Assignment" if my_variable.nil?
The goal of the above is to get to something that a lot of people appreciate: The *aesthetic* of Ruby and its expressiveness. But there's more you can do as well.
Case 2: Packaged Code Distribution: Aka "Gems"
No doubt you've heard of Ruby gems. These things are about as close to DLLs as is possible - but it's so much more. The gem system is a package manager all by itself - and it handles versioning and dependencies quite nicely.
Imagine it this way: you want to use NHibernate on your next project. You've heard there's a new release, so you decide to head over to the project site to download it. You're greeted at Sourceforge with an obtuse download page that you stare at for a bit - ultimately figuring out what it is you need to click on.
Once you have NHibernate and it's army of supporting DLLs, you manually drop them into your project - making sure that you put them in a "lib" directory so they can go under source control - with your web project completely incapable of managing which version the app requires. And if it's in the GAC, you're screwed. Finally you manually add the references and off you go.
Imagine now that NHibernate was ported to Ruby: RHibernate - and is available as a gem. Your installation becomes:
gem install rhibernate
(Rubyist Note: yes I finally know about RVM - that's for another post). The gem manager heads on out and pulls down all the lovely bits you'll need - as well as the appropriate dependencies, and pops them in your gem files. If you're using Rails or Sinatra you can dictate the specific version required by your project - and your project can actually go and get them for you (using Rake, a task runner).
I've heard a few people say they dislike the gem system, and Ruby's openness in general - stating that you end up managing gems and debugging the code a lot, and that this is a hassle. This is reasonable - but I like to look at it the other way: at least I *can* debug it! Moreover, just because Microsoft writes and distributes code doesn't mean it's bug-free.
Case 3: The Simple Things
I was watching a Railscast the other day on seeding data in Rails, and Ryan Bates showed some code which made me do a double-take. It's an interesting thing to ponder - how the various frameworks would handle such a thing.
Let's imagine that there's a text file online with a list of all the countries in the world. I want you to import them into your web app and pop them into a table.
Let's assume for fun that you already have a table and an ORM with an object called "Country" - that part doesn't count. Neither does the mechanism - let's just pretend you have a console app kicked up. The only restriction is that you have to use the core language/platform features...
Here's the Ruby code (from Ryan Bates)
require 'open-uri'
Country.delete_all
open("http://openconcept.ca/sites/openconcept.ca/files/country_code_drupal_0.txt") do |countries|
countries.read.each_line do |country|
code, name = country.chomp.split("|")
Country.create!(:name => name, :code => code)
end
end
A couple of things here: open-uri is part of Ruby, not some external gem I pulled in. It allows you to call "open" which returns the contents of the page. You open a block against that return and loop over the values. Notice the second line of the loop - the one with "chomp.split", showing off Ruby's ability to assign variables in succession...
It's safe to say that with C# and System.Net it's just a bit longer. Not that much longer - but you'd have to have a pretty practiced hand to get through the "machine noise" that begins to creep in. Again - this is an aesthetic, a preference that a lot of people won't value and that's just hot buttery flavah. It's OK to have an opinion and if you don't share mine, I really don't care :).
Working with enumerations in Ruby is pretty nice - once you get used to code blocks and what they do. I'll write more about those later - for now take a look at this code - assume we have an object Address with typical Address-y properties:
zip_codes = my_address_array.collect{|address| address.zip_code}
"collect" is an array method that iterates over an array, "collecting" whatever is inside the code block. It could be, literally, anything - a computation, constants, reassigments - in this case I'm returning a simply property: zip_code. All the zip_codes on my addresses are "peeled off", if you will, and popped into another array - which is the function result.
Case 4: Meta Programming
Every language has the ability to do reflection and introspection - Ruby makes it just a bit easier. Consider the following scope: I want you to build me an ActionInvoker - something like they have in ASP.NET MVC - that searches an object for a method, and if that method exists, invoke it.
With C# you would reflect on the type of the object, grab MethodInfo and interrogate it for the method name. Once found, you could call Invoke, passing along the arguments in a weird incantation of BindinFlags and argument arrays.
With Ruby, it's a bit easier:
def action_invoker(controller, method_name) controller.send(method_name) if controller.respond_to? method_name end
Take a second, if you don't know Ruby, and enjoy how this method is pretty much self-documenting. You don't need to know any Ruby at all as a matter fo fact... In Ruby - "send" and "respond_to?" are core object methods on Ruby which you will invariably come up against when you start learning the whole thing.
Another thing people freak out about is the ability to "train" objects - or to completely override class implementations without any problem. This is really loosey-goosey for some people - but comes in damn handy when you need it.
Consider this: you have some code that interacts with PayPal and you want to test it. Specifically you want to make sure that your payment receiving functionality works - unfortunately PayPal is right in the thick of it, and insists that you need to call them back to verify the payment info before you can execute the final order process.
In C# this means mocking - intercepting the (hopefully public) "Validate()" method on the library. In Ruby this means resetting the "validate()" method:
class PayPalLibrary
def validate
true
end
end
Ruby even allows you to declare things on the fly. Consider Rails' ActiveRecord - you can query your database with ad-hoc methods like:
my_addresses = Address.find_by_state("HI")
Address doesn't have this method - Rails made it up on the fly. I don't know exactly how they did it, but one way would be to define a method for Address (which is called a class method) just in time:
def Address
def self.#method_missing(meth, *args)
#parse meth to build the query, use args to build parameters
end
end
The code above works at a class level - what you can think of as a static method in C#. You can also do this for instances of an object - declaring a method on the fly:
def my_address.format_address #this is sort of like an Extension Method - but it's assigned to an actual instance, not a type end
Why in the world would you create a method and stick it on an object like this? When you don't want to (or can't) work with the class directly - perhaps if it's sealed (which Ruby supports) - or for some other reason. Which is kind of the point:
With Ruby, generally you can.
Firing Up Visual Studio?
Go for it - have fun. I know I'll be getting a load of code-responses and that's OK. Please don't forget that I do know C# - some would say rather well (others know the truth...). I'm perfectly aware that Ruby is not for everyone. Some folks like rules and compilers - others like expressiveness and fun. If you want to turn my comments into a code wasteland - have at it.
My whole point here is to tell you why *I* like it - not to convince you of anything.
In honor of that - let's end with some Ruby haiku from Why the Lucky Stiff (no link)
"eyes".scan /the_darkness/
catch( :in_the_wind ) { ?a.round; "breath" \
or "a".slice /of_moon/ }