Scott Hanselman

Programmer Intent or What you're not getting about Ruby and why it's the tits

May 23, '07 Comments [68] Posted in Ruby
Sponsored By

A user named yesthatmcgurk left a comment on DotNetKicks where he/she said:

I must be a complete loser, because I can't see where Ruby is such hot shit. I'd love to read a story, "What you're not getting about Ruby and why its the tits."

Such a great comment that I had to get involved. One of the other commenters pointed to a post over on "Softies on Rails" that's really worth reading.

Note: Forgive the use of "the tits" in this context. "Slang Definition: A description of something you show great liking to, or greatly appreciate..." Usually not a work-friendly phrase, but perhaps pub-appropriate.

There's a simple snippet of Ruby code:

def shutter_clicked
if @camera.off? || @camera.memory_card_full?
    return
end
  capture_image
end

Ruby folks have their own aesthetic and sense of beauty. They would say that the Programmer's Intent is better expressed like this:

def shutter_clicked
  capture_image if @camera.on? && @camera.memory_available?
end

These two functions identically express the Programmer's Intent and the second one expresses it better, many believe. This one simple example is subtle to some, beautiful to others. TunnelRat says:

What is this obssesion[sic] with "expressiveness"? Go write poertry [sic] if you want to be expressvive.[sic] 

Remember that ultimately our jobs are (usually) to solve some kind of business problem. We're aiming for a finish line, a goal. The programmer's job is translate the language of the business person to the language of the computer.

The whole point of compilers, interpreters, layers of abstraction and what-not are to shorten the semantic distance between our intent and the way the computer thinks of things.

Reginald "raganwald" Braithewaite links to the blog Agile Renaissance that absolutely nails it for me (emphasis mine):

If you survey the over 5000 different languages and dialects that humans speak, you find that there is no universal set of equivalent semantics between them. This fact implies that there can never be a computer language which will always have the shortest semantic distance between itself and any solution. Therefore there never will be a universally best programming language.

I'm just learning Ruby, personally. Like anyone making their way in a new language, be it a programming language or a spoken language, I can make myself understood, but not effectively. I'm certainly not writing poetry, nor am I able to "mince words" in Ruby.

You'll find lots of "smackdowns" on the .NET between different languages. This post isn't a smackdown post. Sure, if your language of choice doesn't have a particular function, it could likely be added.

There are some fun one-liner comparisons though and some folks think that paying a:

Java:

new Date(new Date().getTime() - 20 * 60 * 1000)

Ruby:

20.minutes.ago

In this example, the elegance is a combination of how Ruby works, and a Rails library called ActiveSupport that is a Domain Specific Language that extends Ruby. There's a special satisfaction when you read a well-written novel and you go over a turn of phrase and think, "wow, what a great way to express that. That was a perfect way to describe ____," and there's no ambiguity.

While programming, unless you have 100% code coverage via Tests, there's ambiguity. There's a lack of clarity in expressing what you intended vs. what you might get.

Cyclomatic complexity is just one of many software metrics that can help you understand what your code "says" it's going to do. Remember, your code always runs exactly as you wrote it.

Cyclomatic complexity may be considered a broad measure of soundness and confidence for a program. Introduced by Thomas McCabe in 1976, it measures the number of linearly-independent paths through a program module.[Carnegie Mellon SEI]

The most important word there, to me, is confidence. Can you be confident that your code is written to be express what you intended? If you have full coverage, there's a better chance you understand what it can and will do (although achieving full coverage guarantees nothing, but that's another post). If not, it often helps to have a language that makes expressing your intent very clear, concise, and above all, unambiguous. Unambiguous expression of intent gives you (and your customer) confidence that things will happen as expected. There are some things easier expressed in Zulu than in English. Folks who run Ubuntu should know that.

When programming (that is, expressing your intent to the computer) you should select a language that matches up with the program you're trying to solve. Every language is, in a way, a Domain Specific Language.

Regardless of what your language of choice is, you might be someone who says all languages eventually become, or try to become Lisp, or you might think Visual Basic is the best or that PHP is God's Language, you should learn a new language every year. I code, and have coded, for many years in C#, and before that C++, but it's important for me in my personal development to remember why I learned Haskell and Lisp (I suck at Lisp) and Smalltalk, and why I return to them occasionally for a visit.

This year, I'm learning Ruby. Does that mean my team is moving to Ruby?  Probably not, but it does mean I'm learning Ruby this year because I believe in sharpening the saw. You might be too busy sawing to sharpen, but I'd encourage you - no matter what brand or type of saw you use - to remember that there are other folks out there cutting wood successfully with a different kind of saw. Maybe they know something that you don't.

Either way, it's clear that the jury is still out on all these technologies. Hedge your bets and learn as much as you can. There's more than one way to express yourself. Give it a try.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, May 23, 2007 7:05:44 AM UTC
Good thoughts there Scott, although I'm not sure if you've really explained why it's the tits. I agree with you that is surpremely important to keep "sharping the saw" as you so nicely put it.
I think another interesting point to look at might be how well the code can be maintained in the future. For most of the code that gets written in the world, the original developer won't be around years later when someone needs to go back and make a bug fix or extend the code in some new way. By reducing the distance betwen the intent of the code and what is expressed in the code, it likely makes it easier for someone else to maintain and modify the code in the future.
That said, there's some benefit of the maintainer not needing to learn a new language just to fix a bug or extend some code. While there may never be a perfect language for all solutions, there may be advantages to using one language over another that go beyond the immediate needs of the programmer writing the code today.
Wednesday, May 23, 2007 7:12:23 AM UTC
Good point. When I get time I'll add more about how Rake and Rails are just Domain Specific Languages implemented in Ruby...Ruby seems to be a fine language for creating little DSLs easier than in Python.
Wednesday, May 23, 2007 7:18:02 AM UTC
dirty dirty language. Unsubscribed!

i thought you were one of the clean one's scott. Not like the Rory's and the Carl's....

Just kiddin. Keep doin what you do.
Wednesday, May 23, 2007 8:06:06 AM UTC
The elegance of Ruby is in expressing clearly what your intent is - the example of "20.minutes.ago" is a beautiful example of a clear and perfectly expressed intent, the Java/C# equivalent isn't.

Any half competent developer can scan read your code to see what is going on, the alternative Java/C# version needs few seconds to read and understand.

Which then leads to the obvious refactoring of extracting the Java/C# version into a method called MinutesAgo(20) ... which now is much more readable, but has increased the complexity of the application, and has required half a dozen extra unit tests.

Then there is the desire to take that odd looking MinutesAgo() method, and to create a mini library around it, so that we can do all sorts of date and time manipulations ...


And all because the language we chose to express our logic in was a poor way to represent our intentions.


Of course I accept that "20.minutes.ago" is dependent on a whole library elsewhere, but is is still a lot more expressive than any version that could be done in Java/C#

Wednesday, May 23, 2007 8:46:25 AM UTC
I agree with Scott about it being the right time to learn Ruby.

Of course we should note that C# and VB are evolving, and some of the paradigms that make programming in Ruby so elegant will be available in those languages. For example I could define the following extension methods in C# 3.0 (not compiled so treat as expression of intent instead of working code)

public static TimeSpan Minutes(this int numberOfMinutes)
{
return new TimeSpan(0, numberOfMinutes, 0);
}

public static DateTime Ago(this TimeSpan numberOfMinutes)
{

}
Wednesday, May 23, 2007 8:51:33 AM UTC
- Apologies for early submit

I agree with Scott about it being the right time to learn Ruby.

Of course we should note that C# and VB are evolving, and some of the paradigms that make programming in Ruby so elegant will be available in those languages. For example I could define the following extension methods in C# 3.0 (not compiled so treat as expression of intent instead of working code)

public static TimeSpan Minutes(this int numberOfMinutes)
{
return new TimeSpan(0, numberOfMinutes, 0);
}

public static DateTime Ago(this TimeSpan numberOfMinutes)
{
return DateTime.Now.Subtract(numberOfMinutes);
}

which should allow a C# developer to write:

20.Minutes().Ago()

which has a similar syntactic cleaness to it.

Of course the advantage of learning Ruby is that you begin to see these possibilities.



Wednesday, May 23, 2007 8:56:14 AM UTC
public static class TimeExtensions
{
public static TimeSpan Minutes(this int i)
{
return new TimeSpan(0, i, 0);
}
public static DateTime Ago(this TimeSpan t)
{
return DateTime.Now.Subtract(t);
}
}

ta da! .NET 3.5 is awesomeo!
Harry
Wednesday, May 23, 2007 8:57:06 AM UTC
oops. someone got there first.
Harry
Wednesday, May 23, 2007 9:09:29 AM UTC
20.Minutes().Ago()

which has a similar syntactic cleaness to it.


Indeed the power of C# is constantly evolving, and there is certainly some (debatably limited) application of extension methods. But it is still polluted with the past, in a way that Ruby isn't ... the brackets in your example make it just that little bit less elegant than the Ruby version.

The date example used was a good example of why Ruby makes Java seem really clumsy, but of course C# 3 does it much better, but Scott did take it from a page (which he linked) with a dozen other examples, and most of those are just as clumsy in C# 3 as they are in Java.

Having said that, I'm a big fan of C#, and not only because I can write some fancy reflection stuff using delegates and be certain that I have a job for life as nobody else will ever be able to understand it! :) But I can still appreciate why I should be using Ruby sometimes ...
Wednesday, May 23, 2007 9:29:33 AM UTC
I've seen some ruby code, but this line just blew me away:

capture_image if @camera.on? && @camera.memory_available?

But the .NET 2.0 version of 20 minutes ago isn't too bad either:

DateTime.Now - TimeSpan.FromMinutes(2)

Looking forward to .NET 3.x where extension methods will make it possible to do some neat things too.
Hermann Klinke
Wednesday, May 23, 2007 9:34:37 AM UTC
Completely agree, learning (or at least exploring) another language is a great way to broaden your horizons, even if you never plan to use it for "work".

There's a nice little Ruby tutor application for Windows called Hackety Hack - I found it opening my mind to two languages, both Ruby and English - it's very well written.

My take on it:-

http://www.feedghost.com/Blogs/BlogEntry.aspx?EntryId=3717

Or just go straight there:-

http://hacketyhack.net/
Wednesday, May 23, 2007 11:01:42 AM UTC
Hi Scott,

Not sure of the value of being the zillionth commenter, but I'll try to keep it short:

I think the 20.minutes.ago example is getting a little too close to the language of the domain (too far from the language of the computer). 99% of the world's programmers will look at that and think the word order is wrong. Does int have a method that returns datetimes? If so, I imagine it must have a pile of other methods to return n of other things. I need to spend some time learning how this works. It's cute, but confusing. Should I write an "extension method" (or ruby equivalent) to have int return n domain objects? 20.Customers.SendOffer? This seems kind of silly to me, plus it's so far from the language of the computer as to be a little distracting. There is still a huge semantic leap between the computer and the language in exchange for a smaller leap between the language and the domain.

One more thing: I think programmers are starting to latch onto this idea of domain specific languages being unambiguous. As domain specific languages are more like human languages, I think they are actually more ambiguous. What does a tranche mean for example? It depends on a lot of specifics of the problem domain. Mortgages? IPOs? The term itself is loaded with ambiguity. It's so ambiguous, we had to go to French to even find a word we liked. I don't think there is anything unambiguous about DSLs, but they do get problem statements out of word documents and into code, which I think is one thing that makes them powerful.

Sorry for not keeping it short. Thanks for sharing your thoughts with us!
Wednesday, May 23, 2007 11:49:18 AM UTC
Why is Ruby so cool?

Dynamic typing is the no-brainer. You can save a lot of extraneous typing using statically typed languages with type inference (e.g. OCaml), but nothing beats a dynamic language for flexibility. And of course, mixins rock (for example, you can write a DI engine or an AOP system in just a few hundred lines of code).

Everything is an object is really nice as it is a cleaner model than having both objects and primitives. For instance, it makes it much easier to implement things like custom data types so if you want to define a given property as a SSN, you can call a validate method against a users SSN property easily.

The support for metaprogramming is both the strength and weakness of Ruby. It is great to be able to do, but the explosion of in language DSLs will (I believe) tend to lower maintainability as average developers get their hand on the language. Still, I like the ability to be able to write my own in-language DSLs - much less work than ANTLR and for domains that require a combination of 3gl code and DSLs, it is easier than some of the round tripping work Krzysztof Czarnecki is working on for Java.

I don't think the Ruby world really has a great maintainability story yet. Even in the world of Domain Specific Modeling we're only just coming to grips with issues such as evolution of non-orthogonal DSLs, and I think that as you start to evolve the abstract grammar of different DSLs at different rates reused across hundreds or thousands of projects it's going to get pretty interesting pretty quick.

Still, Ruby is opening people up to metaprogramming and it is fun. Looking forward to a chance to actually use it in anger when clear my backlog!
Wednesday, May 23, 2007 1:05:35 PM UTC
While I agree that you should sharpen the saw with a new programming language, I myself have gone through several transitions of languages and working environments, C++, Perl, ColdFusion, Java, .Net.

The issues I have these days is that there are that with so many new API's being released within .Net itself, it's hard enough keeping up with vNext implementations which will at least sustain my current livelihood. I feel like honestly I don't have time to try and pickup Ruby or boo because I'll not be able to pick up WCF, WPF, Workflow API, BizTalk, .Net 3.5 (and all it's syntactic changes). Then all the 3rd party component API's I have to use in an enterprise environment. Then trying to run a mISV on the side, to get out of the rat race. I praise you or anyone else for finding the time. My brain feels tiny.

Robert
Wednesday, May 23, 2007 1:15:18 PM UTC
Scottt, I know you are a big fan of "leaning on the library" and Ruby's library feels immature compared to .NET. I think the comprehensive set of classes and utilities you get with .NET far outweight ROR and Ruby, at the moment. Sure, certain areas of ROR are very mature, such as ActiveRecord. But I found myself in odd territory when I wanted to load data files into a db using pure Ruby. Debugging and the lack of a good IDE at the time made me come back to .NET.

For me, the .Net platform has more functionality and power than ROR, even if it is still catching up in the "syntactic sugar" dept.
Wednesday, May 23, 2007 2:05:07 PM UTC
Of course, Scott's right that learning new languages and new libraries is a wonderful way to expand the mind and make you a better programmer in whatever your language(s) of choice is (are).

Still, the "20.minutes.ago" thing just looks like magic to me and I don't understand what it's doing at all. Further, I can't tell a great deal of difference between Ruby, Python, JScript and Python, nor do I understand the value of fully dynamic languages all up (I like using the compiler as a spellchecker for my code).

That said, my question is, is there something unique about Ruby that enables it as a platform for Rails (which I really do like) and if so, what is it?
Wednesday, May 23, 2007 2:10:53 PM UTC
@Mason:

Your reasons are exactly why we're creating IronRuby. Debugging support comes 'for free' courtesy of the DLR + the underlying platform / tooling. As do the libraries. You're correct that the quality of libraries are not uniform in any open source world, however Ruby does have quite a few very nice libraries as well - open-uri, rake, AWS::S3 come to mind.

Wednesday, May 23, 2007 2:55:55 PM UTC
@Chris: You nailed it with the compiler as the spell checker for your code. I feel the same way. When I delete a variable that is still referenced, I want to see it show up at compile time, not at run time. I know the ROR purists say unit testing should prevent this scenario, so maybe I am too old school.

@John: I played around with ROR in Fall of 2006. Not sure what the state of IronRuby was at that point. Anyway, I am interested in checking out IronRuby.
Wednesday, May 23, 2007 3:15:17 PM UTC
>capture_image if @camera.on? && @camera.memory_available?

Ok, when Ruby has this the fan boys all cream their jeans.

But when Perl has it, it's a sign of obscure syntax and Larry Wall's lack of good taste.

I think I get it.
Bobbie The Programmer
Wednesday, May 23, 2007 3:28:27 PM UTC
No mention of blocks?

They are what I miss most when not programming in Ruby.
Reed E
Wednesday, May 23, 2007 3:29:07 PM UTC
I've posted my comments via a post What you're not getting about programming.
Wednesday, May 23, 2007 4:04:53 PM UTC
I'm all for expressiveness. But I'm not yet convinced that Ruby achieves it in a good way. For example, the ability to do iteration like so:

10.Times{print "hi "}

Very compact, very concise, and almost natural language as read. But expressiveness does not equate to proximity to natural language (ask a mathematician), and clean syntax does not equate to clean structure. Both are important, but I'd say clean structure has a deeper and more lasting impact. On that note, I think that in the particular example of this method of iteration, there are dubious design decisions....

It doesn't make sense to me that iteration functionality would be a member of a class representing an integer. Yes, iteration is inherently numeric and discrete in nature, but an integer doesn't "do" iteration. Iteration is not a behavior of a number. It is just a behavior that involves a number. And even so, it doesn't _need_ to involve a number. It could involve a condition. Or it could just wait for a "break". Maybe we could make an iteration class that provides all these types of iteration. Or maybe, since iteration _always_ happens over a code block, we could make iteration a member function of a "code block" class, and the iteration type/condition/break-event would be the parameter. These other possibilities make as much, if not more, sense to me than making iteration a member function of an integer class.

I think the thing that makes me uncomfortable here is what seems (at least to me) to be a "willy-nilly" intermingling of functional and object-oriented features without thought for clean structure. Just because we can do something in a functional manner doesn't mean that we can throw good OO design out the window.
Waterbreath
Wednesday, May 23, 2007 4:11:59 PM UTC
> although achieving full coverage guarantees nothing, but that's another post

No worries, I already wrote that post. ;)
Wednesday, May 23, 2007 4:13:15 PM UTC
I've actually been using that post to show off Ruby to a few co-workers and in doing so, have realized that I've been writing C# in Ruby for some time now.

I think that the biggest piece of why Ruby is "the tits" is it's expressiveness. Ruby is such a natural language - it reads like a sentence and that goes a long way to helping others grok the code and it's intent. "The speed of a project is proportional to the speed of transfer of ideas between minds." and Ruby goes a long way to grease those wheels.
Wednesday, May 23, 2007 4:27:49 PM UTC
@David - I'm doing the same thing. I also tend to speak English in Spanish. I get word order wrong and my Spanish flows like English. But, that's typical when one learns a new language.

There was a great session at RailsConf (a bunch of session were snoozers) called Clean Code. I hope it was recorded. The guy showed a simple command line parser...for doing things like foo.exe -l 5 -g "foo" and it was basically written in C#-style Ruby, then he applied some Rubyisms and some OOP to it and step by step, it got smaller and smaller.
Wednesday, May 23, 2007 4:41:15 PM UTC
@Chris: Its hard to demonstrate the beauty of Ruby in a simple comment, so I won't try. You are right that Ruby and Python a fairly similar. They behave in many of the same ways. (As does C# and VB.NET.) You can definitely take the lessons learned from Rails and apply them to other web frameworks built in other languages. But much of the beauty of Rails has to attributed to Ruby. Ruby's meta-programming capabilities enable much of the "magic" we see in Rails. The frameworks that borrow from Rails rarely match the simplicity and elegance of Rails because the other languages are not as expressive as Ruby.

I'd encourage you to investigate further, because Ruby is kinda like the Matrix. Nobody can tell you what the beauty of Ruby is, you have to see it for yourself.

You are right to use the term "magic". We also use it in the Rails community. Its not always obvious what the code is doing, but it is there to better express the programmer intent. For the record.

20 => 20
20.minutes => 1200
20.minutes.ago => Wed May 23 10:18:13 -0600 2007

P.S. It was great to meet you last weekend.
Wednesday, May 23, 2007 4:48:09 PM UTC
Chris S.:
"is there something unique about Ruby that enables it as a platform for Rails (which I really do like) and if so, what is it?"

Meta-programming. Ruby, through it's use of blocks, symbols, and reflection, makes it much easier to write programs that write programs. You could write Rails in any other language, but it seems to me that Ruby, and the general idioms that go along with Ruby, makes it easier.

Bill Katz had a posting about it. http://www.billkatz.com/node/42
Wednesday, May 23, 2007 5:11:08 PM UTC
Well, I understand all that, I read and understood the Pickaxe, your examples are quite nice but I still find Ruby ugly. Sorry. But if it works for you, sure, use it.
What's annoying me just a little bit is when people just assume that if you don't like it, it must be that "you don't get it". Because you know, it's the One True Final Language that will put an end to all other languages. Like so many before it.
Wednesday, May 23, 2007 5:13:57 PM UTC
python > ruby
Wednesday, May 23, 2007 5:20:52 PM UTC
>capture_image if @camera.on? && @camera.memory_available?

Just curious, would you have to change that statement at all if you later needed to add an else condition?

Also, couldnt your first example have just been writen as:

def shutter_clicked
if @camera.off? && @camera.memory_card_full?
capture_image
end

Personally I prefer it written this way anyways because its quicker to process if I need to look at capture_image at all.

But the 20.minutes.ago example was nice.
Sean
Wednesday, May 23, 2007 5:23:25 PM UTC
Ruby is nice, but the fundamental problems is that intention is lost as long as the degrees of freedom of the language stay as ridiculously high as they are with ruby. Reading code that looks like English in some ways makes things worse because it makes what you're doing more vague. ("What kind of User is this, we only have, oh, I dunno, 12 different meanings for User")

http://blog.intentionalsoftware.com/intentional_software/2006/06/a_process_with_.html

Programming languages are not the answer. Being able to always go meta might be.
Greg
Wednesday, May 23, 2007 5:28:45 PM UTC
Scott,

I started programming as a classic asp scripter so I didn't have a strong OO foundation (well, I'm still working on it).

When I started learning C# VB.Net I had to learn the "patterns", go over a learning curve of writing OO (like creating local variables that the object's properties functionality will access).

Isn't true that code generators are great and popular because the programming languages (name it Java, C#, VB.Net, etc.) force you to repeat a bunch of code that a 'better' language can avoid?

Simplicity in 1 line is cool, but what about the overall picture of OO approach with a language like Ruby, Python? Can you write another article extending on that? That would be great

Great article Scott

Rafael
Rafael
Wednesday, May 23, 2007 5:37:26 PM UTC
Also, couldnt your first example have just been writen as...


Absolutely, but I the return was added presumably to show the kind of "nested code" that folks (myself included) often have to write in short methods. You know, If, else, mid-function return...

Wednesday, May 23, 2007 5:51:54 PM UTC
"but I still find Ruby ugly."

Me too. All the "def" and "end" statements. I kind of like the use of whitespace in Python a little bit better. But all in all, I'm a curly brace man. If I could spend a solid 24 hours straight playing with it, (I'd (probably love( Lisp))). I like using punctuation to set off blocks of code.
Wednesday, May 23, 2007 5:53:12 PM UTC
Rafael - Great idea. I'll look into it.
Wednesday, May 23, 2007 6:25:47 PM UTC
Good post. Would just point out that the java one-liner
new Date(new Date().getTime() - 20 * 60 * 1000)
wastefully creates a second Date object. It would be more sensible to say
new Date(System.currentTimeMillis() - 20 * 60 * 1000)
not that that's ANY better asthetically and an approach that's conceptually more inline with the Ruby equivalent would involve using many lines of code to manipulate the Java Calender class which is universally reviled and probably has the worst API of any class I've come across in Java.... Manipulating dates is painful with it and it does stupid things like myCalendar.getTime() // returns a Date object
even though
myDate.getTime() // returns a long representation of epoch time. So you can end up with this steaming turd: myCalendar.getTime().getTime()
Yes, you could just say myCalendar.getTimeInMillis() but that doesn't answer why Calendar.getTime() is completely different from Date.getTime() or why Calendar.getTime() returns a Date.

All of which just confirms what you were saying. Ruby's IS "the tits"... at least when compared with Java.
Wednesday, May 23, 2007 6:42:39 PM UTC
The java example should probably be: System.currentTimeMillis() - 20 * MINUTES;

which to me is actually cleaner looking than:

20.minutes.ago

In ruby I would prefer:

minutes(20).ago

for the following simple reason:

Assuming that we have a left-to-right associative reference operator (which is required to be readable, as 99.99% of all programmers expect this to hold), then 'minutes' is an operation OF numbers. That's bass ackwards. It should be minutes.20, or minutes(20), or minutes[20], or any other form of reference. The way to pass 20 as a parameter to 'turn into minutes' is a detail not important for the sake of clarity. What IS important is simply that the relation between the two is 'correct': 20 is a 'parameter' to the 'operation' of minutes. "20.minutes.ago" suggests that minutes is an parameter of '20' which is programatically speaking awkward, like an awkardly formed english sentence. It's ugly. Whether this is a function, a built-in command, library call, that distinction is not important for the sake of what I'm trying to get across.

20.minutes.ago looks pretty because it happends to coincide with english. I'm a native dutch and english speaker - I've lived for long periods of time in english speaking countries but I was born and currently reside in the Netherlands. I 'think' in english when I'm writing this. I would think in dutch when speaking to a (dutch) friend. I don't translate in between. If you are bilingual, you know the feeling. If not, perhaps you can imagine how this works. You don't translate, you just translate thought into speech or writing and back. The 'dutch' part of your brain isn't on when speaking english.

I also 'speak' programmer, in a fashion. I think in programmers terms when I look at code, and I don't think in english, or dutch, or any other natural language. Take a look at e.g. the OR function in programming languages. I never ever have to think about whether or not this means 'inclusive' or 'exclusive' or, the way you always have to in english. My nerve endings just know, instinctively, what it means. I don't need to think about it, just like I don't need to translate dutch to english, or how you don't need to think about how your brain parses depth by comparing what your two eyes see. You just KNOW that object A is further than B. (Sometimes this knowing is actually wrong, see optical illusions, but that's a discussion for another time).

Thus, "20.minutes.ago", while it looks very pretty in an english piece of writing, looks like a hag's warty nose in a programming language that has a left-to-right associativity; it's the wrong way around!

If ruby were right-to-left (like Reverse Polish Notation calculators, or some stack-based languages), "20.minutes.ago" makes more sense. Ruby isn't right-to-left, however.

It's hard, for obvious reasons, to declare that "minutes 20" makes more sense than "20 minutes" when all your language neurons, currently firing at full clip because you are reading english, say that "minutes 20" is ridiculous, and "20 minutes" is natural, which makes your argument seem so self-evident. I notice you didn't give any proof for why "20 minutes" is more readable than "minutes 20", which goes to show how easy it is to assume it's 'just easier to read, because!'.

Ruby mixes and matches ordering in other constructs. Where python for example mostly gets it right (with the odd exception, such as the 'character'.join() method of linking the elements of a list together by infixing the character in between each entry), ruby is far less dedicated to the left-to-right ordering of things.

Take conditionals. You can order them both ways - "if [item] [action]" is allowed, but so is "[item] if [action]".

The parts of my brain that intrinsically know programming languages, the fact that there's a choice is all wrong and ugly to its sense of aesthetics. I used to read a lot of latin and the things you can do, poetically, by the extreme sentence reordering flexibility of the language, is great stuff. But programming languages aren't verbal languages. Things that work in verbal languages are avoided like the plague (like ambiguous operators - there's a reason there's no "either inclusive or exclusive or, figure it out from context" operator in any programming language except perhaps some maelbolgish exercise in creating a monster), and in many ways, vice versa.

Have you ever looked at a large applescript program? It's eminently english readable. Those things are an absolute pain in the tusch to write, because you never remember how it all goes. Other languages that tried to claim "this language is so much like english, it's easier!" all fail in practice, because programming is hard, and trying to make it look like english doesn't help. You've probably heard of "write only programming". There's also such a thing as "read only programming" and it's just as bad. It's a bit like saying that writing poetry in interlingua is easier. It really isn't, eventhough there's a good chance hundreds of millions of people, many of those having never read a line of interlingua in their lives, will understand the simplistic meaning of what you wrote. It LOOKS easy to a passerby.

Making code look good to the wider audience is a pointless and futile exercise. It needs to be readable and simply understandable to those who 'speak the jargon'. - Programmers need to read that code. So, write the one language they must understand before even starting on your project: The language you're writing in.
Wednesday, May 23, 2007 6:49:09 PM UTC
Two things to be careful of when building (or building on) meta programming concepts are:

1) Making sure that you don't confuse "overly clever" with "clean and clear".

There are lots of good examples where meta programming can deliver very clear code that is much more readable. I think ActiveRecord and LINQ are two examples of how you can use these concepts to make database programming much simpler and easier to code/read/refactor.

There are also a lot of examples where developers (especially junior ones) become so enthralled with condensing 2 lines of code into 1 that they end up obscuring or complicating the intent of the logic. This is a syndrome I call "trying to be overly clever", and it happens with both static and dynamic language today. I always preach that it is much better to write 2 lines of code that are instantly clear, then 1 line that makes people have to pause and think for 10 seconds to understand what it does.

One issue to watch out for with dynamic languages is that you can really go overboard with clever tricks when writing code. While these can be really cool when you are writing the code the first time, they can lead to excruciatingly hard bugs to fix later when someone not familiar with the technique or language feature ends up introducing regressions or problems without realizing it. Anyone who has inherited a large Javascript file from someone enamoured with using/absusing Javascript to its fullest probably knows what I mean....

2) Making sure you understand how to version and bring forward code written with meta-programming concepts.

I'd argue that developers are still trying to understand how best to evolve frameworks that build heavily on meta-programming concepts. One challange with frameworks that use dynamic typing and injection features is how to write code/modules that plug into them and which then don't break when the framework revs or new features or modules are injected.

This issue has hit Rails really hard the last 12 months. There have been several incremental updates of the core Rails framework that have broken the most popular extensions and plugins significantly. Compatibility and performance issues when mixing modules have also increased significantly. In a quick glance through the rails-talk forum you'll find a large % of questions are related to this.

Javascript frameworks on the client also run into issues like this (although they seem to be a little more rare).

The lack of compilation checking with dynamic languages can make finding and fixing these issues harder (and despite all the TDD claims of Ruby devs, they still seem to be making unexpected changes that break people significantly).
Scott
Wednesday, May 23, 2007 7:16:21 PM UTC
@Reinier: I understand your point. It is well thought out and consistent with how .NET and Java base libraries work. I respect those design decisions.

However, the thing that Ruby allows you to do it to place the responsibility where it belongs. You are stating that the responsibility for shifting the current date value by "n" minutes belongs to the DateTime/Date object. In Ruby you can put that responsibility on the Integer object. You may not agree with that approach, and this may not be the best example, but you have the flexibility to put it there if you choose. Its up to you.

You can always do it the C#/Java way if you wanted. But part of the experience of learning Ruby is doing it the Ruby way...

then = Time.new - 1200 # 20 minutes * 60 seconds = 1200
# vs.
then = 20.minutes.ago
Wednesday, May 23, 2007 7:20:34 PM UTC
Man, I love Ruby. Man, I love Rails. Man, I hope I never have to deploy it in a production environment again. That was like...ANTI-tits.
Wednesday, May 23, 2007 7:26:42 PM UTC
@Mike - exactly, that's how I figured it out, too...or at least how I got it to make sense to me.

20 is an integer, all alone. But, it's a comment thing to use integers to quantify a time, like minutes or hours. Very common - datetimes are complicated things, but ultimately can be express in dozens of ways, often composed of some kind of number, so why not blur the line?

20.minutes, now we have a "time spam" and the data type is minutes. Add .ago, and we've do subtraction from now. Feels nice and totally flows left to right, while it just so happens to look like English.

Again, this is less and less about Ruby, as ActiveSupport (the lib that adds this) is DHH's language playground. We've seen that if we (the C# community) cared to do this kind of thing, adopt this kind of aesthetic, we could.

As with all Art, it's subjective. Not everyone appreciates the mona lisa. Looks like a typical portrait to me. ;)
Wednesday, May 23, 2007 8:22:01 PM UTC
20 is an integer, all alone. But, it's a comment thing to use integers to quantify a time, like minutes or hours. Very common [...] why not blur the line?


It still feels wrong to give the integer the responsibility for turning itself into a timespan. Especially if our ultimate goal is just to bring the syntax closer to natural language. (Though we need to be careful about that, because natural language has a lot of variability... There may be a language where Minutes(20) reads more natural than 20.minutes.) I really don't think it's a good idea to turn the structure/flow of the algorithm or data on its head to achieve this.

I would actually be more comfortable with a member-of operator that works right-to-left to increase readability of some statements instead of this responsibility inversion. For example say we use something like (x, y)->Func and Func<-(x, y) to say pass the arguments x and y into the function Func. And say we use .> as a left-to-right member-of operator and <. as a right-to-left member-of operator. We would need more parentheses for clarification to the compiler... but if we can be confident that the parentheses and such are only for the compiler, and that the statement should read like natural language, then it's easy for the brain to just pretend they aren't there.

Then we could do things like this:
(20)->Minutes.>Ago
((20)->Minutes)->Before<.Now
Waterbreath
Wednesday, May 23, 2007 9:12:21 PM UTC
<?php
$d = strtotime("20 minutes ago");
?>
juji
Wednesday, May 23, 2007 9:22:25 PM UTC
Well, doing anything 'the xyz' way, where xyz is the language, is very important. That's also why the whole 'learn a new language in 2 weeks' schtick never works. But I digress:

code can be very readable but also very hard to write. By arbitrarily going for parameter->operation and operation->parameter ordering so that it 'looks nice' is needlessly complicated.

Not to mention that the number of operations on numbers are now so enormous that you run into namespacing problems. minutes still works allright, but .seconds does not.

does 20.seconds translate to a TimeInterval of 20 * 1000 millis, or,

does it translate to a Degree type that represents the angle covered by '20 seconds'?

20.library.name.for.date.stuff.seconds is no longer very pretty at all.

In practice, stuffing multiple methods onto a single class (in this case, integer), it's annoying and usually not possible to 'namespace' the methods. Contrast this to trying to namespace separate functions or classes, and most languages offer plenty of namespacing options.
Wednesday, May 23, 2007 10:22:18 PM UTC
I agree we need to learn on language a year. Instead of programming language debate...i'll just offer that I'm learning Python this year. The django framework and python together seem to be a pretty powerful platform. Good luck with your ROR experience!
Wednesday, May 23, 2007 10:33:03 PM UTC
@Reinier: Its not a matter of parameter->operation or operation->parameter. In Ruby everything is an object, and you can send objects messages asking them to do something for you. In the 20.minutes.ago case you are sending the following messages to the following objects:

# Hey 20, you are a Fixnum and I'd like to call your "minutes" method.
# I could call your hours or days methods, but minutes is fine for now.
span = 20.minutes
# Cool, span now equals the numeric value for 20 minutes.
# Ruby follows Unix and this span is in seconds, not milliseconds or ticks.
# span is

# Hey span, you are also a Fixnum with a value of 1200.
# I'd like to call your "ago" method.
# Will you give me a Time object for now minus your value (in seconds)?
then = span.ago
# Groovy, now then is a Time object with the value of "Wed May 23 16:12:15 -0600 2007"
Thursday, May 24, 2007 3:18:39 AM UTC
To followup my post wherein I hinted at my love of blocks in Ruby, a contrived example:

def reverse_lines(filepath, &block)
IO.readlines(filepath).reverse_each do |line|
s = line.chomp
yield s, s.length
end
end

reverse_lines( ARGV[0] ) do |line, length|
puts "#{length}: #{line}"
end

Note in particular how one can pass a block to a method and have it execute the block as necessary via the yield statement. It has broad application to creating filters, encapsulating database calls, and tokenizing.
Reed E
Thursday, May 24, 2007 4:40:45 AM UTC
new GregorianCalendar().add(Calendar.MINUTE, -20);
Anonymous
Thursday, May 24, 2007 6:43:39 AM UTC
What's wrong with :

DateTime.Now.AddMinutes(-20);
Julian de Wit
Thursday, May 24, 2007 6:51:58 AM UTC
Syntax can't make me switch.

Rails however is very appealing to me since finally every project will have the same structure.
This way it scales in terms of adding more developers.
Also I like the simplicity of convention over configuration.

Having a simple c# on rails (not that bloated like monorail :( ) would be great.











Julian de Wit
Thursday, May 24, 2007 7:26:55 AM UTC
@Mike: I know - see my second comment. Perhaps if I rephrase parameter->Operation vs. operation->parameter ordering to 'which object has the responsibility for the operation' this works better. In this case, 20 has the responsibility. Which is ugly. The DateTime library has responsibility.
Thursday, May 24, 2007 7:48:02 AM UTC
DateTime.Now.AddMinutes(-20);


The 'problem' is that the ruby version reads very clearly to even a non-programmer (I bet your mother could tell you what that line of code did)

The C# way reads OK to us developers, but is essentially reversed... to scan it requires that you either build the objects in your mind as you scan:

I have a DateTime thing, ok, I want that to represent Now, and now I want to add some minutes, ahhh now I want to add -20 minutes, which means that the add becomes a subtract of the positive value, so that is 20 minutes, removed from the DateTime value of Now .... got it ....

The Ruby way reads:

I want the time 20 minutes ago ..... got it



A contrived example though the 20 minutes example is, I can see the elegance. That said, to me languages like Ruby and Python are a little too unstructured and loosely typed (I use that term loosely here btw) for my general usage, which often involves trying to instill junior or 'less talented' developers with some kind of sense of how not to stuff up my architecture ... in C# it holds your hand tight and positively drags you through the experience .... for my daily use, Ruby lets developers have just a little too much free will for my liking :D

Thursday, May 24, 2007 8:01:26 AM UTC
If it works for you, great. I looked at Ruby for this project of mine and found the language absolutely appalling. I finally chose Python because its so powerful, reasonably fast and above all, readable. There seems to be too much magic in Ruby to appeal to me.
Thursday, May 24, 2007 12:30:34 PM UTC
Sigh. I had high hopes, so I guess its only natural to feel a little let down. Apart from a little syntactic sugar, it seems that what I'm not getting (at least from this post's perspective) about Ruby is that its fun and good for the noggin to learn new programming languages.
I can't argue that it's not true. I just want to know why Ruby should not only be a new toy I should put in my chest but should also become my platform of choice. How does Ruby make my job easier? Clearer qualifies as easier, but I just don't see syntactic nuances as being the be-all-end-all. Hell, every sweet little trick in Ruby that I've seen can be reproducible in .NET via extension methods (the example you gave can be done via two extension methods--one for ints to convert to timespan, then one for timespan converting to datetime.now() - this).
Thursday, May 24, 2007 1:40:26 PM UTC
@Casey
Do I want my mother(read: business types), to be able to understand the code. Bravery begins there, and then you get the business types trying to write code. The result is late 90's free perl scripts, only in ruby. Bunch -o- crap you have to sift through to find decently written code.
Robert
Thursday, May 24, 2007 2:05:44 PM UTC
Trackback n' stuff: Ask and ye shall recieve (blog software is in beta, sry)
Thursday, May 24, 2007 2:53:26 PM UTC
Code that my mother can read?

Oh please. If that's the purpose of Ruby's syntax, it's a crappy language indeed. Glad that that isn't it.

You can't make programming simple for non-programmer types. It just can't be done. Making your code look english-like is no substitute whatsoever.

See my previous arguments on how in 'programese', minutes.20 is much more readable and neat versus 20.minutes, which is unreadable and ugly.
Thursday, May 24, 2007 3:05:25 PM UTC
"Hell, every sweet little trick in Ruby that I've seen can be reproducible in .NET via extension methods"

Yeah, but you have to wait for C# 3.0 to ship and then write the extension methods. ;) The nice thing about Ruby is that the sugar isn't just syntactical. It's there to make your code sweeter. Mixins, blocks, yields, iterators, all these things make Ruby a better language than a lot of the other languages out there. Sure some of those are present in current implementations of C# and Python and others are coming in future versions. But Ruby is here, now. IMO, the one thing that Ruby is missing that would make it sweeter is named parameters. You can hack them in using arrays as arguments but ehh.

I have yet to write a Rails application for work. I've done a few prototypes in Rails, with the actual application being written in ASP.NET. But I use Ruby and Python all the time for maintenance scripts and data loading/migration. The scripts end up much less verbose than the batch files or VBScripts I used to write. I got tired of writing out C# console applications just to move some files around or load some data into a database.

Case in point. I downloaded some Visual Studio code snippets a few months ago. The extensions were wrong on the XML files and I couldn't import them into VS. So I wrote a Python script that found the files in every subdirectory, renamed the extension, and moved the directory to the correct VS directory. It was about a 12 line Python script. How much code would an equivalent C# console application have in it? How much of it is boilerplate?
Thursday, May 24, 2007 4:22:17 PM UTC
Thanks, Scott, for an excellent article.

And thanks to your commenters, as well, for a bunch of really excellent comments.

When I start reading an article online such as this one, and decide at some point that it is really something I need to finish, I clip the title to my Google Notebook, just in case I am interrupted and cannot get back to it soon.

Usually, Notebook-wise, that's the end of it.

For this article I had to clip two great quotes near the end as well: the "When programming (that is, expressing your intent to the computer) . . ." paragraph and the "This year, I'm learning Ruby. Does that mean my team is moving to Ruby?" paragraph.

FWIW, I've always planned to learn Ruby and ROR so I could really call myself a Web Developer. Now, as of "20.minutes.ago", actually, I'm much more excited about that idea!

Thanks again.
Thursday, May 24, 2007 4:42:38 PM UTC
@Reinier

Very well said. Programming languages are not English, or Dutch, or Spanish. Each one is its own languages with its own concepts of what looks "normal". If I am an english speaker, Student.Book looks like "student's book", which is "normal" for me. But in spanish, the word order word be "book of student"; now Student.Book is backwards. Is this a problem? No! When I am programming in C#, I am not speaking English or Spanish, I am speaking or C#. There is quite simply no need to make the programming language look like a natural language; its called a "language" for a reason, because it has its own syntax and semantics.

@Casey: "Of course I accept that "20.minutes.ago" is dependent on a whole library elsewhere..."

This is the whole point. The only reason that you like the Ruby version better is because someone else has done the job of creating and testing the method for you. This has nothing to do with the advantages of the language.

@Mike: "the thing that Ruby allows you to do it to place the responsibility where it belongs"

This is completely backwards. Object-oriented design principles say you should put the responsibility where it belongs. What Ruby allows you to do is put the responsibility wherever the heck you want to. It may be really cool for the first programmer to be able to organize things however he thinks is best, but it becomes a nightmare for readability and maintainability for the SECOND programmer to try and read the mind of the first. Standards are good. Consistency is good. Just like I can't construct English sentences using any word order I choose, neither should I be able to construct programming statements in any order I choose. I should be bound be a set of rules which forces me to express myself in a way that can be understand by anyone else who is familiar with the language. Being understandable by any average Joe who happens to know English should NOT be a design goal of a programming language.
Thursday, May 24, 2007 6:58:27 PM UTC
Rumor is that Borland will be releasing a Ruby IDE laster this year (they just released one for PHP).
Steve
Thursday, May 24, 2007 9:02:01 PM UTC
@Steve - Yes, you can get it at CodeGear
Friday, May 25, 2007 7:01:31 AM UTC
@David - I accepted the example used was actually poor. It was a good example when it was written against the Java or C#2 equivalent ... but Ruby forced C# to create extension methods, hence why they are in C#3.0 - and for that alone, Ruby is good for us all.

But the reason Ruby is good, is not the libraries, it is the structure of the language, as the other examples Scott linked to more than amply demonstrate.

David:
Being understandable by any average Joe who happens to know English should NOT be a design goal of a programming language.


Developers are human too, and readability should be a core design principle of all programming languages. I'm an experienced developer in C# and have a good familiarity with Ruby, and I can still read Ruby faster than I can read C#.

The reason I choose C# usually is nothing to do with that, it is to do with what my clients demand, what I think will enforce the best discipline on the team, and what has the best tool and community support. But at some point Ruby will catch up on all of these fronts. Then the decision becomes harder - unless the next greatest language has arrived by then :)



Friday, May 25, 2007 2:56:09 PM UTC
@Casey: I read through the other examples Scott linked to, and I still see no advantage of Ruby over C#, even without extension methods, but especially with them. I guess I just don't "get it".
Tuesday, May 29, 2007 4:46:56 PM UTC
The quote about there being 'no universal set of equivalent semantics between' all of the languages is kinda wrong in that there is the concept of Universal Grammar (http://en.wikipedia.org/wiki/Universal_grammar) that Chomsky and others discovered. Though 'grammar' and 'semantics' are not equivalent of course, there is a thread that does connect all languages.

I like Ruby as a computer language because its easier to build DSLs on it like 'RubyOnRails', which for all intents and purpose is a DSL for web applications. Plus Ruby is far more concise then Java and C#.
Ned
Thursday, May 31, 2007 8:29:26 PM UTC
A point of style: generally, usage of "sic" is reserved for cases where the reader might be confused about who exactly said the word or phrase in question. Where a phrase is inserted into the flow of text, it's a good idea to so indicate errors. In the case of a quote set in a block, it's fairly clear who's responsible for a spelling or grammatical error, especially when it's a quote of a comment or message board post :).

I mention this not because I take others' usage personally (if that were the case, the Internet would've been the death of me long ago), but because excessive usage of sic tends to lend an unintended patronizing tone to a piece of writing. I've had that issue myself in the past.

More details at AskOxford, SparkNotes, et al.
Friday, June 01, 2007 12:49:24 PM UTC
What I don't get is..

for (p=someptr;p==NULL;p++)..

LOL..

Languages has its strength and its weakness.. anyway ruby interpreter is built on top of C.. so which is better? ruby or C? nobody can't say because each has its own stregth. It's pretty hard to make websites full of ajax with pure C, but it's impossible too to make high performance 3D simulation with Ruby at FULL SPEED.
Luna
Sunday, July 15, 2007 3:16:21 PM UTC
Since Ruby allows you to express yourself, why is English such a bitch? Why can't I 'self clear way express way in any clear possible'? It makes a lot of sense to me! All kidding aside (just kidding), if you let English, or any other language, drip into the programming language you're in for a treat because now you're setting the tone. The next time somebody from China will add to the code they will inject their grammar into the mix. Maybe minutes.20.since.ago sounds better for some folks. Or ago.last.minutes.20 seems prettier for the next door neighbor. Point being that spoken languages from a logical point of view are plain ugly. They are the result of thousands of years of every idiot throwing their expressiveness into the mix and forcing others to memorize their latest 'creation'. Here we go again...
PBZ
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.