From: David Masover Date: 2009-02-11T03:42:34+09:00 Subject: Re: Ruby vs Perl performance Robert Klemme wrote: > Then you probably haven't used Struct enough. For me the code above is > just > Foo = Struct.new :some, :random, :args > Ah, thanks. That should help. >> 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? It does, actually, assuming you called it with a hash of options. Perl objects are most commonly a blessed hash. Granted, you'd probably want to do a bit more sanity checking -- the above won't complain if you pass a string instead of a hash, or if you misspell an argument, etc. What it doesn't solve is the part where you'd use setters -- it's the equivalent of directly setting the instance variables. > > >> 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 > Maybe I should read the docs, but as I understand it, in Javascript, I can do this: f = Foo({ a: 'some_string', b: 12345, c: function() { // some method } }); I could even do: f = Foo(new Bar()); thus creating a spontaneous new child of both Foo and Bar. Keep in mind that both Foo and Bar are classes, each of which may have methods of their own -- in this case, Bar's methods (or properties) override Foo's. To get something even close in Ruby, I'd have to do this: f = OpenStruct.new class << f def c #some method end end And the other example would look more like this -- but only if Bar was a module, and not a class in its own right: f = Foo.new f.extend Bar And, since it must be a module, it's not really the same thing. Again, this could be faked with something elaborate: class Foo def initialize(klass) meta = class << self; self; end obj = klass.new obj.methods.each do |method| meta.class_eval do define_method method do |*args, &block| obj.send method, *args, &block end end end end end Note that a subclass of Delegator won't work here (if it had a chance?), as that would have Foo override Bar, not vice versa. And even this doesn't do what we want, as rather than including methods of both classes in a kind of poor-man's multi-inheritance, we're in fact delegating to another object. >> 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... > I haven't touched Perl in any meaningful way in over a year. When I took a break from Ruby for that long, it took awhile for me to get fluent and sure of my Ruby syntax, also. >> 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. > One method per module! Great idea. Doing a functional approach is better, but still missing the point. The point is that the person packaging the code might not know ahead of time that you need that method, or that it's a useful method to extract on its own. That leaves you with a choice of either patching their code (best long term approach), or copying and pasting, or doing some _really_ crude hacks, if it's even possible. > Otherwise there's something wrong with how code is packaged IMHO. > Probably. But for the same reason I like being able to monkeypatch Ruby classes, I also like being able to hack Javascript in this way. It means I can get something working, quickly, without immediately having to dig into the other code and patch it. >> 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. > There are, however, libraries to make the OO less painful in Perl. To take a Ruby example, Rails likes to autoload classes and modules by overriding const_missing. I could argue, then, that you need const_missing to get basic functionality, that of not having to require a dozen files at the top of each file. But if I'm working on a Rails app, it's nicely tucked away for me. When I'm not, I wrote a simpler version using Kernel#autoload -- autoload isn't sufficient by itself, but this worked well enough. And yet, I've provided at least a few places where that metaprogramming (or hackery) is more difficult than it needs to be in Ruby, or sometimes flat-out impossible. No, I don't expect it to be easy -- it probably shouldn't be. But we're talking about a language where I can have a method reach up the call stack and evaluate text in the scope of its caller. It's a language where I can redefine pretty much any operator, on pretty much any class -- I can make 2+2=5, if I really want. Or I can define nil.nil? to be false, or Object#nil? to be true, and make the interpreter crash. Next to all of that, the ability to rebind a method to an unrelated class seems relatively sane. You could even have UnboundMethod#bind keep doing what it does, and add an UnboundMethod#bind! to do the more "dangerous" thing. >> 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 as, in Java, I shouldn't miss duck typing, because if they're really related, they should share common ancestry. > 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. > Again, I'm not seeing how this has any more potential for disaster than duck typing or metaprogramming. Those have as much capacity for misuse. Now, not doing that often is probably a good idea -- not using Kernel#eval often might also be a good idea. But I don't like it when a tool tells me what I may or may not do, simply because it's not a good idea. > 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. I tend not to build things that don't rely on at least a few external modules. I would rather have a language flexible enough to build things like this as an external library, in the language itself, than one that has everything built in. In fact, that's one complaint about Perl (or PHP) -- they both have a lot built in, and a lot polluting the root namespace, for things that may have once been very common tasks. I believe Perl has finally moved the report syntax into a different module -- keep in mind, Perl is called practical extraction and _report_ language, but they decided reports no longer belong in the core language. I know I'd be (slightly) happier with PHP if templating wasn't a part of the core language. > 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. Unless, of course, they're already there on CPAN. > It matters because some poor > maintainer has to read all the code and understand in order to apply > his changes. > That's a bit like saying we shouldn't use Rails, because any potential maintainer will have to learn Rails as well as our own codebase. And that is somewhat true -- Symbol#to_proc wasn't in core Ruby at the time. But I think it's reasonable to make the core language smaller, and push things into frameworks, so long as the syntax doesn't suffer. (That's a big reason I prefer Ruby -- syntax.) I'm not trying to sell Perl's OO as the best thing, or even "better". But I'm defending it as inherently less useful than Ruby's. Someone might as easily say that Ruby's OO is inherently less useful than Java's, because Java does stricter (static) type-checking.