4 ms·
Why is 'symbol table hack' a 'bad' word? There is absolutely nothing wrong with the way symbol tables are used in python.
by kashif 18y ago
Why is 'symbol table hack' a 'bad' word? There is absolutely nothing wrong with the way symbol tables are used in python.
- earthboundkid 18y agoMore to the point, it's a feature, not a bug. That everything is a symbol table hack makes metaprogramming much simpler.
- kashif 18y agoYes indeed. You have phrased it better.
- tptacek 18y agoWhat'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".
- EliAndrewC 18y agoI 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__ ?
- tptacek 18y agoYes: make this evaluate as Python (in Python idiom, of course): @bb = Bblock.make { push ebx mov ebx, dest pop ebx retn } That's all method_missing.
- inklesspen 18y agolike EliAndrewC said, __getattr__ is Python's version of method_missing: http://docs.python.org/ref/attribute-access.html http://docs.python.org/ref/attribute-access.html And no, that's not all method_missing. looks like method_missing plus a block.
- tptacek 18y agoHow do you catch and transform method arguments with __getattr__? For instance: mov ebx, [ebp+4] needs to detect that ebx matches a Register class constant and that [ebp+4] is not an array, but rather a register indirection expression.
- inklesspen 18y ago__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.
- tptacek 18y agoThe 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
- ubernostrum 18y agoOK, 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)
- jnicklas 18y agoI 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"
- ubernostrum 18y agoWell, 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.