From: Robert Klemme Date: 2009-02-11T01:53:30+09:00 Subject: Re: Ruby vs Perl performance 2009/2/9 David Masover : > Robert Klemme wrote: >> IMHO Perl makes OO unnecessary hard. > > Very true. Most CPAN modules manage it anyway, these days, but I agree -- OO > doesn't have to be that hard. > > It is fun, though. Well, everybody as they like. :-) > For the same reason, I also find it kind of cool that Perl objects are > typically just hashes with methods attached. Ruby objects, while effectively > the same thing, tend to hide instance variables away. I like that, it's a > cleaner approach, but it is still fun to take a hash of options, perhaps > filter them, and then bless them as an object. > > Occasionally, this actually is more convenient. For instance, in Ruby, I too > often find myself writing code like this: > > class Foo > attr_reader :some, :random, :args > def initialize some, random, args > @some = some > @random = random > @args = args > end > end Then you probably haven't used Struct enough. For me the code above is just Foo = Struct.new :some, :random, :args > Or worse, let's say I don't like positional arguments (and I don't): > > class Foo > attr_reader :some, :random, :args > def initialize options > @some = options[:some] > @random = options[:random] > @args = options[:args] > end > end Foo = Struct.new :some, :random, :args do def initialize(options) members.each {|m| self[m] = options[m]} end end > Or worse, say I've written some setters that do something magical. I then > want to set those if they've been passed in: > > class Foo > attr_reader :some, :random, :args > def initialize options > self.some = options[:some] unless options[:some].nil? > self.random = options[:random] if options[:random].nil? > self.args = options[:args] if options[:args].nil? > end > end Foo = Struct.new :some, :random, :args do def initialize(options) members.each {|m| x = options[m] and self[m] = x} end end > Yes, I could do some metaprogramming. I should stress that I do prefer Ruby > to Perl, for exactly that reason -- if this ever gets too annoying, I can > probably do something like the following, which has probably already been > done somewhere: > > module AutoInitializer > def self.included klass > klass.extend ClassMethods > end > module ClassMethods > def auto_init *args > include(Module.new do > attr_accessor *args > define_method :initialize do |options| > args.each do |arg| > if options.has_key? arg > self.send "#{arg}=", options[arg] > end > end > end > end) > end > end > end This is not needed as I have tried to show above. > Now my class is only this: > > class Foo > include AutoInitializer > auto_init :some, :random, :args > end > > That's arguably better, but a bit more work at the beginning. Still, it's > worth comparing to the Perl solution: > > sub init { > my($class, $self) = @_; > bless $self => $class; > } But this does not set properties, does it? (My Perl is a bit rusty nowadays.) If this is the case then this is not a solution to the same problem. > Granted, there are better ways to do that. It's certainly going to get > hairier if there are going to be setters involved. But that is one of the > fun side effects of what, at first, seams like a haphazard, tacked-on > design. Ok, *I* do not get fun from this - especially when I have to do it over and over again... > JavaScript is similar, in some respects. Suppose someone passes me in a hash > of options. Well, hashes are objects, so I can just do this: > > function Foo(obj) { > for (var property in obj) { > this[property] = obj[property] > } > }; > > Bam. Not only instant options, but instant extensibility -- nothing prevents > a user from passing in a function to override one of mine, thus creating a > singleton descendant of my class. -> OpenStruct > I'm going to stop now, because this is getting a bit long, and the core > point hasn't changed -- I like Ruby, and I see how this kind of stuff can be > done in Ruby, but I wouldn't immediately dismiss these other object systems. >>> >>> my $foo_like_thing = Bar::new(); >>> Foo::bar($foo_like_thing, $some_other_arg); >> >> The current object is passed in as an argument, meaning this is just >> another subroutine -- it lets you do tricks like this: >> >> What does this? Does it create a Bar and then initializes it as Foo? > > No, it creates a Bar, and calls Foo's bar method on it, if I've gotten the > syntax right. *if* - LOL If even _you_ do not know... >>> Kind of like Javascript's call() and apply() -- and I'm not even sure >>> this can be done in Ruby. For all the duck typing goodness, I can't seem to >>> figure out how you'd unbind a method and rebind it to something of an >>> unrelated class, unless there's an explicit tree of inheritance. >> >> Why would you want to do that? There's a reason why both classes are >> unlrelated, i.e. chances are that the method would not work in the other >> class / object. If you want to simply share code then you can use modules >> which is a much cleaner and safer way to do it. > > Indeed, modules are usually the saner choice. However, I have done this > _often_ in Javascript. Probably the simplest example might be the common > each loop: > > function each(array, func) { > for (var i in array) { > func.call(array[i], i); > } > } > each(['one','two','three'], function(i) { > // now 'this' is bound to the value > }); > > Granted, that's a toy, but it is more convenient that way. And in Ruby it's already built in. > And then there > are the cases where you want to do something clever -- say you have multiple > superclasses: > > var Bar = { > // one big pile of funcitons > } > var Super = { > // another big pile of functions > } > obj.foo = function() { > if (i_want_super) { > Super.bar.apply(this, arguments); > } else { > Bar.foo.apply(this, arguments); > } > } > > Maybe some of those are actually superclasses. Maybe they're modules, and > you only need a single method, not the whole module. If you need a single method only that should go into its own module. Otherwise there's something wrong with how code is packaged IMHO. > Either way, I would put the burden back on you. Why is this so dangerous? > Why is it any more dangerous than the other duck typing tricks Rubyists use > every day? Because in Ruby hackery (or call it "metaprogramming") is an add on, i.e. you can use it in special situations, while in Perl you need to do it to get basic things (OO) to work. > Why shouldn't I be able to do: > > a.method(:foo).unbind.bind(b) > > when a and b aren't related, but I happen to know they share a common theme? Because this is an indication that your code is not well structured. If they share a "common theme" this theme could be packaged into a module and properly documented. Just moving one method over is an ad hoc solution which - when applied with only moderate frequency - will give unreadable and thus unmaintainable code very soon. > After all, what ties the method to the object -- isn't it mostly going to be > calling instance methods, and occasionally accessing instance variables -- > so why should 'self' be exempted from the "quacks like" rule? If you need only this single method you can as well use a functional approach, just define def foo(obj) obj.instance_method_a = 1 obj.another_method end No need for unbind bind hackery. >> The problem with this is: you _have_ to build it yourself. If I only get >> the basic building blocks and have to reapply them over and over again to >> get the same result (a bunch of classes with methods and state) then I am >> wasting time. > > And then you discover one of the most basic tools in any language: A > library. Even better: have it already in the standard library or language. Even if it's on CPAN already, you still need to add this other external module to your project etc. You can't save Perl's OO that way. > Take my above AutoInitializer example. I could complain that I have to > reinvent it every time, but clearly I don't. I can just file it away in a > file called autoinit.rb, and if it turns out to be original, I can upload a > gem. > > Or I can decide to use openstruct instead. > > What matters is how powerful those basic building blocks are, and what it > looks like when you're finished. It matters because you might take longer because you need to first build complex structures out of your basic building blocks because they are not present in the language. It matters because some poor maintainer has to read all the code and understand in order to apply his changes. Why should I choose a language with broken OO when I want to do OO if I can have one with good OO, a marvelous clean syntax which has similar other capabilities (scripted, dynamic, built in regular expressions etc.)? If this is fun for you then be it. But please do not try to sell me Perl's OO as a great thing - because it isn't. Cheers robert -- remember.guy do |as, often| as.you_can - without end