views:

451

answers:

7

I've been using Java almost since it first came out but have over the last five years gotten burnt out with how complex it's become to get even the simplest things done. I'm starting to learn Ruby at the recommendation of my psychiatrist, uh, I mean my coworkers (younger, cooler coworkers - they use Macs!). Anyway, one of the things they keep repeating is that Ruby is a "flexible" language compared to older, more beaten-up languages like Java but I really have no idea what that means. Could someone explain what makes one language "more flexible" than another? Please. I kind of get the point about dynamic typing and can see how that could be of benefit for conciseness. And the Ruby syntax is, well, beautiful. What else? Is dynamic typing the main reason?

+4  A: 

Things I like

  • less code to get your point across
  • passing around code blocks (Proc, lambdas) is fun and can result in tinier code. e.g. [1, 2, 3].each{|x| puts "Next element #{x}"}
  • has the scripting roots of PERL.. very nice to slice n dice routine stuff like parsing files with regexps, et. all
  • the core data structure class API like Hash and Array is nicely done.
  • Metaprogramming (owing to its dynamic nature) - ability to create custom DSLs (e.g. Rails can be termed a DSL for WebApps written in Ruby)
  • the community that is spawning gems for just about anything.
Gishu
What do you mean by "nicely done"?
Dinosaur Jones
http://www.ruby-doc.org/core/classes/Array.html v http://java.sun.com/j2se/1.4.2/docs/api/java/util/Arrays.html - Ruby array has all the operations as ready methods: mappers, iterators, stack ops, filtering, selecting, reversal, sort, and so on.. java would req you to write the same for loop n times
Gishu
+6  A: 

It's also because it's a classless (in the Java sense) but totally object oriented (properties pattern) so you can call any method, even if not defined, and you still get a last chance to dynamically respond to the call, for example creating methods as necessarry on the fly. Also Ruby doesn't need compilation so you can update a running application easily if you wanted to. Also an object can suddenly inherit from another class/object at anytime during it's lifetime through mixins so it's another point of flexibility. Anyways I agree with the kids that this language called Ruby , which has actually been around as long as Java, is very flexible and great in many ways, but I still haven't been able to agree it's beatiful (syntax wise), C is more beatiful IMHO (I'm a sucker for brackets), but beauty is subjective, the other qualities of Ruby are objective

Robert Gould
+2  A: 

Mixins. Altering a Ruby class to add new functionality is trivially easy.

Don Werve
+2  A: 

Duck typing refers to the fact when types are considered equivalent by what methods them implement, not based on their declared type. To take a concrete example, many methods in Ruby take a IO-like object to operate on a stream. This means that the object has to implement enough functions to be able to pass as an IO type object (it has to sound enough like a duck).

In the end it means that you have to write less code than in Java to do the same thing. Everything is not great about dynamic languages, though. You more or less give up all of the compile-time typechecking that Java (and other strongly/statically typed languages) gives you. Ruby simply has no idea if you're about to pass the wrong object to a method; that will give you a runtime error. Also, it won't give you a runtime error until the code is actually called.

JesperE
+5  A: 

Blocks, closures, many things. I'm sure some much better answers will appear in the morning, but for one example here's some code I wrote ten minutes ago - I have an array of scheduled_collections, some of which have already happened, others which have been voided, canceled, etc. I want to return an array of only those that are pending. I'm not sure what the equivalent Java would be, but I imagine it's not this one-line method:

def get_all_pending
  scheduled_collections.select{ |sc| sc.is_pending? }
end

A simpler example of the same thing is:

[0,1,2,3].select{|x| x > 1}

Which will produce [2,3]

+1  A: 

Just for laughs, a fairly nasty example of the flexibility of the language:

class Fixnum
  def +(other)
    self - other
  end
end

puts 5 + 3
# => 2
August Lilleaas
Redefining the plus operator to perform subtraction is certainly flexible. The right kind of flexible? As you said yourself, that is fairly nasty. But that code is also remarkably simple. Thanks for that example.
Dinosaur Jones
That scared me actually. In the wrong hands, that could mean world destruction.
Dinosaur Jones
woooow, this is scary!! I hope no one ever writes a NuclearBomb Management System in Ruby.
hasen j
oh wait, even in C you can #define if while
hasen j
+6  A: 

Dynamic typing doesn't come close to covering it. For one big example, Ruby makes metaprogramming easy in a lot of cases. In Java, metaprogramming is either painful or impossible.

For example, take Ruby's normal way of declaring properties:

class SoftDrink
  attr_accessor :name, :sugar_content
end
# Now we can do...
can = SoftDrink.new
can.name = 'Coke' # Not a direct ivar access — calls can.name=('Coke')
can.sugar_content = 9001 # Ditto

This isn't some special language syntax — it's a method on the Module class, and it's easy to implement. Here's a sample implementation of attr_accessor:

class Module
  def attr_accessor(*symbols)
    symbols.each do |symbol|
      define_method(symbol) {instance_variable_get "@#{symbol}"}
      define_method("#{symbol}=") {|val| instance_varible_set("@#{symbol}", val)}
    end
  end
end

This kind of functionality allows you a lot of, yes, flexibility in how you express your programs.

A lot of what seem like language features (and which would be language features in most languages) are just normal methods in Ruby. For another example, here we dynamically load dependencies whose names we store in an array:

dependencies = %w(yaml haml hpricot sinatra couchfoo)
block_list %w(couchfoo) # Wait, we don't really want CouchDB!
dependencies.each {|mod| require mod unless block_list.include? mod}
Chuck
Why is your SoftDrink example 'metaprogramming'? Isn't that just a different sytax for getters and setters than we use in Java? In Java that would be, oh I don't know, an extra dozen or two dozen lines of code. But it would do essentially the same thing. Right? I'm not sure I'm getting your point.
Dinosaur Jones
Actually, come to think of it, I'm not even sure what 'metaprogramming' means.
Dinosaur Jones
Oh wait, I think I get it now, the define_method is actually setting up accessor methods on the fly. Ok, that's cool.
Dinosaur Jones
Precisely. Metaprogramming is code that writes code. You can take repetitive patterns and turn them into simple declarative statements.
Chuck
And then instance_variable_get and set are actually setting up new instance variables in the class based on those symbols, name and sugar_content. Oooooh. So you've got your getters and setters and all instance variables dynamically. Woohoo! I learned something today! Cool stuff!
Dinosaur Jones
I think I get your dependencies example. You're filtering one array based on another. But the tricky part is the require keyword. Now, does that mean you are adding "require yaml", "require haml" to your code file?
Dinosaur Jones
Oh, ok, I found it, require is a method - it actually loads a file called 'yaml', 'haml', etc. as long as the filename is not in the block_list array.
Dinosaur Jones
You say: A lot of what seem like language features (and which would be language features in most languages) are just normal methods in Ruby. Are you talking about 'require'?
Dinosaur Jones
Yes, I was using require as the example of a seeming "keyword" that's actually a method and can be used as dynamically as any other method.
Chuck
mod, the file being read, is just an argument to require. I'm not sure what's dynamic here.
Dinosaur Jones
The point is that you don't need to know the file at the time you write the require statement. You could have a require on a list like that and add to or alter the list any way you like. That's what I was trying to illustrate with the "filtering."
Chuck
I understand now. Thanks for all the help.
Dinosaur Jones
Another nice property of `require` being a method is that it can be overriden like any other method. For example, RubyGems, package management system for Ruby, overrides `require` so that it looks inside the RubyGems package repository in addition to the normal Ruby load path. There is a library which overrides it so that you can do stuff like `require 'http://github.com/user/project/blob/master/lib.rb'`, i.e. load libraries from a webserver instead of the filesystem. A project for packaging Ruby applications into a single file overrides it to load files out of a zip archive. Another similar
Jörg W Mittag
project, targeted at commercial Ruby applications overrides `require` such that it loads the libraries out of an encrypted SQLite database, i.e. `require 'net/http'` gets turned into something like `eval(DB.exec('SELECT code FROM libraries WHERE name = "net/http";'))` under the covers.
Jörg W Mittag
Another important feature, which is actually required (no pun intended) for the `attr_accessor` example to work, is *executable class bodies*. In Java, a class declaration is just that: a static declaration. In Ruby, there is no such thing as a class declaration. It is a class body, which is executable code just like a method body and which gets executed immediately when it is encountered. This is important, because otherwise the `attr_accessor` method would never get executed. In fact, class bodies even have a return value! (It's the return value of the last expression inside the class body.)
Jörg W Mittag
And inside the class body, `self` is bound to the class object itself. Executable class bodies are pretty cool, because you can do things like define methods in a loop or (more realistically) inside an `if`-expression. Something like `if on_windows then def run(cmd) system("CMD.EXE /k #{cmd} end else def run(cmd) system(cmd) end end`.
Jörg W Mittag