What's a metaprogramming problem that's easy to solve in Python, and hard to solve in Ruby? Here's a metaprogramming tactic that is surprisingly annoying to duplicate in Python: "method_missing".
OK, show me how -- in Ruby -- to define a class, such that the class and all of its subclasses will have some special non-standard behavior (say, gaining or losing attributes or methods based on other things in the class definition)... without re-opening the class after its original definition or otherwise monkeypatching it once it's defined.
In Python you can fiddle with the machinery that creates class objects and use it to do whatever you need. In Ruby, as far as I know, that's simply not possible.
(and, yeah, you need to look up the attribute hooks in Python, because it looks kinda silly when your cherry-picked examples are of things you actually _can_ do in Python)
I am not entirely sure what you mean, but adding or removing methods from Ruby classes is extremely simple and there are a number of hooks in place that allow you to play with the class definition on inheritance. But here is another example of the power of Ruby metaprogramming. The example is sort of nuts, but something similar is actually used in the Camping microwebframework.
Blah = {
:foo => proc { puts "foo" },
:bar => proc { puts "bar" },
:baz => proc { puts "baz" }
}
def Monkey(method)
klass = Class.new
klass.class_eval do
define_method :shout do
Blah[method].call
end
end
return klass
end
class Chimp < Monkey(:foo)
end
class Gorilla < Monkey(:bar)
end
a = Chimp.new
a.shout # => "foo"
b = Gorilla.new
b.shout # => "bar"
Blah[:foo] = proc { puts "I have changed" }
a.shout # => "I have changed"
Well, except the example you've given isn't really "metaprogramming" in the sense I'm trying to get at. Here's a Python translation which shows why:
def Monkey(word):
class _Monkey(object):
def shout(self):
print word
return _Monkey
class Chimp(Monkey("foo")):
pass
class Gorilla(Monkey("bar")):
pass
c = Chimp()
c.shout() # prints "foo"
g = Gorilla()
g.shout() # prints "bar"
In other words, the Ruby code you've provided is basically a class factory taking advantage of closures. The Python example shows that a bit more clearly.
What I'm looking for is something equivalent to Python's "metaclass" declaration, which lets you completely control the way the class object is created. I've been told by fairly knowledgeable Rubyists that there isn't really an equivalent, and that after-the-fact monkeypatching or factory solutions like yours are the only way to come at the problem in Ruby.
Ruby doesn't have metaclasses, this is true. It won't let you subclass 'class'.
However it will let you subclass 'Module', which is the superclass of Class, and you can use this to achieve similar ends if not quite as elegantly.
The way class methods work wrt to inheritance is a bit different in ruby though, and gives you quite a lot of power to provide special behaviours to subclasses via class methods which can be called in the subclass class definition. The kind of "we provide a DSL for you to use in your class definitions to configure the subclass" tricks which (IIRC) tend to require metaclasses in python, don't require them in ruby.
There is also an 'inherited' class method callback on Class, which you can use to run code whenever a new class inherits from your class. And this can be used to achieve a lot of things that pythonistas might use metaclasses for.
On the whole I think anything possible in one language is possible in the other when it comes to metaprogramming, it's just a matter of how you go about it and how elegant it feels.
Anyway, this setup is in no way different to ruby's super
super("foo")
super("bar")
I really fail to see the point here. What is different?
I simply do not see what the python example shows "a bit more clearly" here. I dont even get where "word" comes from.
Besides, "monkeypatching" is a deragotary term invented by pythonistas who do not understand Ruby or think that by adding complexity you can feel "meta" programming so clearly. In my opinion the metaprogramming demanding people are wanking hippos that continually feel a need to add needless and useless complexity on top of problems and in situations where there simply should not be any of that.
Both ruby and python are way to complicated. Can you believe this? At the same time, both are a LOT more elegant than languages like perl or php. Perl 6 still has not managed to get rid of the useless ; at line ends. They managed to eliminate () around if's.
Wow. I am soooo impressed. In 100 more years Perl 7 will be THE best language arrr!!!
But it is a shame that there are not more good languagesl ike ruby or python, because in fact there simply are not that many good languages existing anyway. It takes so much effort to grow a language....
And yes - both ruby and python are very good languages. The fact that there are idiots using the language who have a loud mouth badmouthing either of these two decent languages is annoying, but one can not do much about it.
For everyone else I recommend reading the "why it does not matter" blog. It really summarizes a lot of why ruby and python fill a very similar ecosystem, behaving like rivals who continually try to point out how incredibly stupid the other language is.
Grow up. Use what you prefer, and end this stupid war.
>> def Monkey(word):
>> class _Monkey(object):
>> def shout(self):
>> print word
>> return _Monkey
Note that Monkey is a function that takes a parameter word. So every invocation of Monkey creates a class that will always .shout() the parameter word.
> Anyway, this setup is in no way different to ruby's super
> super("foo") super("bar")
No.
>> class Chimp(Monkey("foo")):
>> pass
>> class Gorilla(Monkey("bar")):
>> pass
You'll note that these two class definitions create a class Chimp that's descended from a class like _SomeMonkeyThatSaysFoo, and a class Gorilla that's descended from a class like _SomeMonkeyThatSaysBar.
That is what the parent poster has challenged anyone to reproduce in Ruby.
> Grow up. Use what you prefer, and end this stupid war.
Programming language wars are at least as old as the Internet, so the admonition to "Grow up" hold no water in this case.
I could quite reasonably parody your closing statement as: "Grow up. So who cares whether a hammer or celery makes a better tool? Just leave us alone, hammer-users."
module SpecialBehavioursForClass
def extended(klass)
klass.class_eval { ...do anything you like to classes including these behaviours as though you're in their class definition block itself... }
end
def special_class_method
...
end
def give_this_class_an_attribute(x)
attr_accessor :x
end
end
then
Class MySpecialClass
extend SpecialBehavioursForClass
give_this_class_an_attribute :abc
end
(note how there's already a class method 'attr_accessor' which does this - but just to reinforce how you'd go about making something similar yourself)
I haven't used Ruby enough to have used method_missing, but I have used Python's __getattr__ to accomplish the same thing. Can you give an example of something that would be easy to solve in Ruby with method_missing but hard to solve in Python with __getattr__ ?
__getattr__ needs to return a callable (usually a function, but you can 'call' other objects as well). And that callable will be called with the arguments to the method.
You can do all that stuff with python, though the syntax isn't as loose; you can't omit the () around method calls which is what I think you're doing here in Ruby. That restriction tends to improve readability and reduce bugs at the cost of making it harder to have a truly transparent DSL.
The parens thing is unfair; obviously, without parsing strings (avoiding which is the whole point of metaprogramming), you can't express assembly in Python.
But isn't it surprisingly annoying to do method_missing in Python? Again, you have to catch the exception case to the symbol table lookup, and cons up a callable that knows to catch the arguments the right way.
Here's the entire method_missing call I needed to make x86 work inside a Ruby block:
def method_missing(meth, *args)
k = Rasm.const_get(meth.to_s.capitalize)
# If it's a class, it's an assembly opcode;
# else it's a register or operand.
if k.class == Class
@insns << (k = k.new(*args))
else
k
end
return k
end
Well, Python has the * args and * *kwargs thing too. So "knows how to catch the arguments the right way" isn't any more of a problem than it is in Ruby. I'm guessing you don't know much about Python; you keep saying "It must be hard not to be able to do X" when in fact you CAN do X.
No, it's not surprisingly annoying. You can override just about anything within the rules of the Python syntax. You can override what << and + and | all do. You can override [] and ().
You just have to live within the Python syntax, which unfortunately for DSLs is stricter than Ruby syntax. But when you're not doing DSLs, that stricter syntax is usually a good thing.
Instead of saying it "...isn't any more of a problem than it is in Ruby" and "I'm guessing you don't know much about Python", why don't you actually show us some Python code which does something similar? I know Ruby well but Python only rudimentarily, so I'm curious what the rough equivalent would look like. If you don't feel like doing that, fine, but I'm just going to ignore your arguments.
His example is too incomplete to translate properly (since I don't know the details of other stuff he's referencing), but the basic idea is this:
A callable object in Python (e.g., a function or method, though not necessarily limited to these) can take advantage of two special options in declaring its argument signature. One is a feature shared between Python and Ruby: you can prefix the final argument with a single asterisk, which will be interpreted as "accept any number of additional positional arguments, and store them as a list in a variable of this name".
So, for example:
def print_args(*args):
for arg in args:
print arg
Which does pretty much what it looks like it should do; you pass any number of arguments, and it echoes them back, each argument printed on a separate line.
The other part is something Ruby doesn't really support, because Ruby doesn't have a true analogue of Python's keyword arguments: using a double asterisk says, essentially, "accept any number of keyword arguments, and store them as a key/value mapping in a variable of this name".
So, for example:
def print_keyword_args(**kwargs):
for key, value in kwargs.items():
print "%s: %s" % (key, value)
If you then did, say, `print_keyword_args(name='Bob', email='bob@example.com')`, you'd get back the output (ordering of items may vary with the Python implementation, but usually you're not concerned with ordering -- that's what positional arguments are for):
name: Bob
email: bob@example.com
These can also be combined into a standard idiom for a callable which accepts any combination of any number of named and keyword arguments:
def takes_any_arguments(*args, **kwargs):
...
These sorts of idioms are incredibly useful for dynamic programming; for example, I work with (and help develop) the Django web framework, and our object-relational mapper uses the `kwargs` idiom to set up query methods which dynamically adapt the arguments they accept to the particular data model you're querying against.
> That restriction tends to improve readability and reduce bugs at the cost of making it harder to have a truly transparent DSL.
I used to think this too. But, then I actually learned ruby, and it turns out to be pretty easy to discern method calls from local variables, unless you are working with horridly long methods, which is a separate issue.
Remember that an instance variable is prefixed with @ in ruby, so if you see something that looks like it could either be a method call or a reference to a local variable, it's pretty easy to quickly examine the local scope to check. It's usually so obvious that you don't need to, though.
There are definitely some potentially tricky situations (closures, etc), but in over a year of working with ruby full time, I have yet to encounter one.
fun1 x
fun1 x + y
fun1 x + y.foo
fun1 x + y.foo bar
What will happen in this perfectly valid Ruby code? Where do the parentheses belong? Do you know off the top of your head?
While any programmer who would write this deserves to be fired, I can't come up with a good reason why it should be valid to write it in the first place.
Looks contrived, because I dont know many ruby users who would write fun1 for a method. Why? Because in ruby there are methods. You dont find many people doing "fun" for a method - the intent clearly was a "function". But where are they? ;)
Anyway, here is the one that comes to my brain flow naturally. It seems python writers need () in order to feel happy, otherwise they think they get confused about things (hopefully they dont have a small brain):
fun1 x
fun1 x + y
fun1 x + y.foo
fun1 x + y.foo bar
What will happen in this perfectly valid Ruby code? Where do the parentheses belong? Do you know off the top of your head?
Btw you omitted what x and y are. My first assumption is that these must be variables that allow the + method
It does not really make a lot of sense though, give the last example:
class Cat
def initialize(name)
@name = name
end
def foo(how = :h)
case how
when :h
how = 'happily'
when :u
how = 'unhappily'
end
puts @name+' meows '+how+'.'
end
end
y = Cat.new 'Tom'
y.foo # "Tom meows happily."
bar = :u
y.foo bar # "Tom meows unhappily."
x = '"At the end of the day "
x + y.foo bar # "At the end of the day Tom meows unhappily."
etc.. I simply have no idea what fun1 should do. Maybe it will involve Jerry mouse and return a conditional story where it is explained why Tom the cat is unhappy.
Btw this is a really contrived example because I tried to model your stipulation of the above code into this. It really makes no sense at all to use variables without any real idea why one should use that.
Why do people WANT to be complex when simplicity is so much more elegant?