Summary
Ruby and Groovy are contrasted. Despite high hopes and
best wishes for the Groovy project, in this author's eyes Ruby turns out to be the winner for most scripting tasks.
It has taken me quite a while to decide to share these thoughts.
I really like the folks who are implementing Groovy. I found
them to be extremely helpful on the mailing list, and I like their
basic concept. A truly portable scripting language is a
terrific idea. Unfortunately, that portability seems to be acquired
at the expense of the two features that make scripting languages
so seductive: Minimal coding and fast write/test/revise cycles.
On the other hand, even if Groovy eventually turns out to be
terrific, it would still make sense to learn Ruby today--if only
because so much has been written about it. That abundance makes it
possible to become familiar with the strange new constructs like closures
that are so unfamiliar to most Java hackers. So understandnig
both Java and Ruby should make it possible to easily transition
to Groovy, when it makes sense.
I began looking at Groovy with high hopes and great
expectations, but reluctantly had to conclude that it's going
to be the technology of choice only in very limited set of
use cases--primarily ones in which the VM is already running.
For general purpose scripting, the time it takes to crank up
the VM and intitialize the interpreter is a deal killer--as much
as 30 or 40 seconds on my machine. I get better performance
compiling and testing Java code in an IDE, especially if it supports
incremental compilation in a background task.
The start-up delay is sufficient by itself to rule out Groovy as a
general purpose scripting language. But unfortunately, it gets
worse:
Things are still in flux, to the point that many discussions on
the user list are aimed at deciding how the language should be
implemented. That's terrific, from the agile methodology
perspective. It means that user needs are feeding into the design.
But it also means that the basic language design is not yet complete.
Lack of documentation. It's to be expected at this stage of its
evolution, but given that things haven't entirely settled down yet,
it's pretty scary to contemplate using it for anything important.
Mostly unhelpful ANT integration. Ruby got this one right with
Rake--super simple syntax plus the power of the language when
you need it. Groovy has (so far) missed a serious opportunity
here. You can call ANT tasks from a Groovy script, and you
can invoke a Groovy shell from inside ANT, but neither one of
those alternatives has anything like the expressive power or
sheer convenience of Rake. (For more on Rake, see Martin Fowler's
article at http://www.martinfowler.com/articles/rake.html)
Dependence on the Java library. It provides a lot of power,
but utilizing those libraries means the language hasn't provided
the kinds of one-line operations that make it easy to do the simple
things you can do with Ruby--for example I/O and XML parsing.
(For more, see Make, Mailing Lists, and Ruby at
http://www.artima.com/weblogs/viewpost.jsp?thread=152464)
Ruby has some really cool features like modules and structs. Maybe
Groovy does, too. It's hard to say, given the documentation. But
more likely the requirement that compiled Groovy scripts must
function as JVM classes will be a limiting factor in this area.
Then there are features inspired by Unix command shells--the ability
to expand variables inside of a double-quoted string, and the ability
to execute a shell command inside of back-quotes and get back the
return string. Of course, shell commands are platform=dependent. But
in Ruby the very definition of a method is determined at runtime, so
it's possible to code multiple-platform versions with no significant
overhead--because the conditional branch that determines how the
method operates only occurs once, when the method is first read.
Rake may well be taking advantage of the dynamic behavior when it
implements the kind of cross-platform commands you typically need in
a shell script: Commands like cd, mkdir, and mkdir_p (to a make a
directory from a path, and all directories that need to be made to
get to it). To tell you the truth, I don't know any language intended
for scripting can do without such things. (To my mind, those functions
should be part of the standard library.)
Ruby sure isn't perfect. There are many Perlisms I'd rather not have seen,
and it has too many ways to do things, imo. That flexibility is great when
you're writing code, but it makes things harder when you're reading it,
because you have to master the idioms favored by whoever did the writing.
(You can alias things in Ruby and you can even overload operators--so you
could conceivably write something that /nobody/ could read.)
If you need a scripting language, Ruby is a great one.
When it comes to building an application with multiple authors and long
term maintainability, I'd say there's a lot of decision-making to do before
going with Ruby. If you're going agile with a small team that can easily
teach each other its favorite idioms, Ruby will probably work. If you adhere
to a more structured methodology and have a large, long term project with many lines
of code--so you can expect a fair amount of turnover among the people who will
be maintaining it--then Ruby probably isn't a good fit.
Once the problems of documentation and open design issues are resolved,
Groovy may yet find a home in that sort of application-building environment.
It will provide dynamic flexibility, like Ruby, but with full JVM
integration so you can build and use Java classes.
But when we begin to drag in all that machinery, we're not talking about something
that feels like a "scripting" language, anymore. We're talking about something that
lies somewhere between a scripting language and an application programming language. So Groovy may yet find a home, but for the kind of fast development cycles I was looking for, Groovy simply isn't the language. At least not yet.
I still think that a truly portable dynamic scripting language is a great idea. I just
don't see how it can be done in the JVM, except in some sort of server-side setting where the JVM is kept running and initialized, and there is some mechanism to feed it a script on the fly.
But to rapidly develop those scripts, an "on the fly" mechanism is needed for testing, as well. In other words, the JVM has to be effectively part of the operating system--wholly integrated into it so the startup time disappears. Until that's done, I don't see how Groovy can provide the same kind of immediacy that Ruby provides. The other problems can eventually be resolved. But that startup hurdle seems insurmountable in theory, as well as in practice.
Then again, maybe there are some viable usage scenarios I've overlooked.
That would be nice.
>And speaking of dynamic, runtime behavior, Ruby has lambda functions: the ability to generate code on the fly and execute it.
Are you sure this is the definition of lambda functions? I ask because Python has a lambda, which is really a way to create an unnamed function, but it also has the ability to execute any piece of text via exec().
I suppose the question is also more complex in Ruby because when you make a block call you are basically passing it an unnamed function.
The language claims the ability to dynamically generate and execute code, and lambda functions look to be the way that's happening in the Rake examples I've seen. But then I'm still learning my around the language, too...
> The language claims the ability to dynamically generate > and execute code, and lambda functions look to be the way > that's happening... > Whoops. I was off base. You can generate code in a string and use *eval* to execute it. Since the language is ynamic, new methods can be generated that way, and then executed. (I've been reading more in The Ruby Way.)
The lambda keyword is a synonym for proc. It makes an object out of a block, which otherwise isn't really an object. It's not clear yet where that makes a difference, since blocks appear to be automatically converted to proc objects in most of the cases where you use one. (If that *isn't* happening, it's not clear what the difference is.)
Here, the block of code is not defined as an object:
list.each {|x| ...do something with list item...}
But whether it is, isn't, or gets converted to one, the result is the same: The block gets executed for each item in the list.
Using lambda or proc seems to give you a pointer to the block that you can pass around:
myMethod = lambda {|x| ...do something with an X...}
I haven't seen the use case that motivates that feature in an application, but the language seems to be using it a lot under the covers.
Then there are closures. So far, a closure looks like a block that takes advantage of its definition context. The Ruby Way gives this example of creating a closure using a proc:
def power(n) proc {|base| base**n} end
square = power(2) cube = power(3)
a = square(4) # 16 ...
Here, the power method returns the value of the last expression evaluated. In this case, it's a proc object, which can then be assigned to a variable like square.
But apparently, that's just one way to create a closure...and I'm still trying to come to grips with exactly what a closure is.
If I sound a little confused, it's only because I am. But I'm also excited. There is a lot of power here, with a lot of different ways to skin the cat, and things that share some similarities.
For example, we could have gotten the same effect by creating a string that looks like "#{base}**2" and then passing it to eval to calculate a square. For all I know, that's what Ruby is really doing internally. Or not...
Bottom line, the concepts behind eval, block, yield, proc/lambda, and closures appear to be the key to unlocking the power of Ruby's dynamic behavior--not to mention symbols (:x) and it's reflection mechanisms.
Matt wrote: > > Don't most dynamic languages have eval() and/or exec()? > In addition to Ruby, Python and JavaScript/JScript have > it. > Python probably does. I don't see how it work in Javascript, though. Being pretty much an "old school" guy, I have a lot of coming up to speed to do on (what to me are) the newer dynamic features. I mean, I've hacked Forth, but there wasn't a lot of eval'ing going on. And I played with Lisp in grad school, but never got into deeply enough for those kinds of behaviors to become second nature.
So those are the "big things" for me, because they're the new ones that will give me expanded scope and power. For other important features, see my previous post: "Make, Mail, and Ruby". What's left is stuff I'm pretty familiar with--it's just a matter of finding out how the APIs work.
That said, I continue to be impressed with how few lines of code it takes to get things done in Ruby. That's practically the definition of fun.
Groovy is interesting in our Java project exactly because it integrates tightly with the JVM and our application. Was no problem to get permission to use Groovy, as it is a Java lib/app. Reuse and glueing of all of our Java stuff is really a valuable part.
But personally I am a Ruby fan and prefer using that for my different scripting tasks on my workstation and at home, even up to somewhat bigger sized apps. I can create solutions very quick and reliable and sometimes deploy them as exe-files to where it is needed. I'm impressed by the possibilities of Mixins, by Rake, ERb and other very clear and smart stuff written in Ruby. So I can understand your arguments.
Unfortunately the lambda example you gave is exactly the point where Groovy does indeed better than Ruby:
def power(n) proc {|base| base**n} end
square = power(2) cube = power(3)
a = square(4) puts a # => 16
This example is one to one translatable to Groovy:
def power(n) { { base -> base**n } }
square = power(2) cube = power(3)
a = square(4) println a // => 16
But regarding a more functional way of programming, you should have a look at this example, especially with your former LISP experience:
A typical currying example in Groovy:
add = { x -> { y -> x + y }} println add(3)(4)
And its pendant in Ruby:
add = proc{ |x| proc{ |y| x + y }} puts add.call(3).call(4)
The difference is that you have to explicitly use the 'proc' or 'lambda' keyword to create a closure and to explicitly call 'call' to execute it, what makes it a bit less handy in Ruby.
Or to turn it: Block-Closures in Groovy are the main benefit in my eyes compared to Java and I use it very often.
Then there are closures. So far, a closure looks like a block that takes advantage of its definition context.
Exactly. A block is an autonomous piece of code. The reference to variables in its definition context makes it a closure. Thus, even in Java there are closures: inner class instances returned by an outer class instance, referring to that outer class instance's private features. (See "Thinking in Java")
Ruby is a very clean, nice, powerfull language with a very low noise level, but still excellent readability. I really like it.
The problem with Ruby is not the language. The problem is no or Japanese library documentation, C dependencies which make it hard to install some ruby libraries (like SVN bindings) and the low number of libraries. Whenever I start a bigger project in Ruby, after some time I usually run in one or more of these problems.
Working inside Rails is great. Once you need to interface with something outside of Rails, your path gets very rough.
But the main difference between Ruby and Groovy is afaik the fact that Ruby is dynamically reference typed and Groovy is both dynamically and statically reference typed.
Stephan Schmidt wrote: > > Working inside Rails is great. Once you need to interface > with something outside of Rails, your path gets very > rough. > > But the main difference between Ruby and Groovy is afaik > the fact that Ruby is dynamically reference typed and > Groovy is both dynamically and statically reference > typed. > > I wrote more on this difference: > http://blog.reposita.org/?p=4 > Great points. I appreciate your comments. And that writeup is definitely worth a read. It very nicely explains the different things that "dynamic" can mean, with some tremendous examples.