python-style decorators

T

Trans

However, even though I think such a change is a good thing to pursue

That sounds good to me. It would easily solve this problem and make
many other things possible.
(I'm even tempted to revive Suby just to offer this one distinct
feature), I offer this:

Honestly, I half-expect I'm delusional, b/c I'm not sure how this
manges to work, but it seems to do so. Maybe you can use it as a
jumping board.

That almost works! See why:

class X

class Foo

def initialize(klass)
@klass = klass
@x = 5
end

def singleton_method_added(meth)
return if meth == :singleton_method_added
m = method(meth)
@klass.send:)define_method, meth, m.to_proc)
end
end

def initialize
@x = 3
end

def self.fooized
@foo ||= Foo.new(self)
end

def fooized.try
10 + @x
end

end

p X.new.try

If try were really bound to the instance of X, this would produce 13.
But it produces 15.

Fudge Nuts! Well, I knew something was a miss. I know Ruby too well to
expect that to work ;)

And I don't think Pit Captain could even wrangle up a solution to this
one. Oh well. Guess it's back to the method_added or block notation.
Which is too bad, b/c not you have to make a choice between the
readability to the former and the reliability of the later.

T.
 
T

Trans

Still trying to get the problem
I know that you know (do I?) that you can do
module M
def foo; "foo" end
end
class X
extend M
end

where's the catch?

When you want class-level methods that go along with your instance
methods.

module M
def self.foo
@foo ||= {}
end
def foo
self.class.foo
end
end

Class X
include M
end

X.new.foo #=> ERROR

If one could include singletons, the solution would be as simple as:

class X
extend(class << Foo; self; end)
end

But as things are you need some sort of "trick".

T.
 
R

Robert Dober

When you want class-level methods that go along with your instance
methods.

module M
def self.foo
@foo ||= {}
end
def foo
self.class.foo
end
end

Class X
include M
end

X.new.foo #=> ERROR

If one could include singletons, the solution would be as simple as:

class X
extend(class << Foo; self; end)
end

But as things are you need some sort of "trick".
Ok I think I got it, thx, I was indeed trying to make Keith's
def self.fooized
def fooized.try
code work but did not see how this was related to the general problem
of yours. That is because I got confused what is defined where, well
this really is sometimes quite embarrassing, I was sure to get it
done, but no such luck .... :(
Thx
Robert
 
P

Paul Brannan

2. An unbound method (such as what you get by calling
foo.method:)bar).unbind) can only be bound to an object of the same
type as the original receiver. For singleton methods, this means that
they can only be bound to the very same object.

Nodewrap lets you do this, but it's not safe:

irb(main):001:0> class Foo; def foo; puts "HERE"; end; end
=> nil
irb(main):002:0> class Bar; end
=> nil
irb(main):005:0> Bar.add_method:)bar, Foo.instance_method:)foo).body, 0)
=> nil
irb(main):006:0> Bar.new.bar
HERE
=> nil

Paul
 
C

Charles Oliver Nutter

Keith said:
I'd love to hear feedback about this library! If you have any comments
or questions, please let me know.

Depending on how you've implemented it, the declarative style is not
thread-safe. You would need to ensure that the decorators are only
aggregated in the current thread, rather than in the class/metaclass for
example, since they would step on each other in the latter case. This is
because aggregating decorators and eventually applying them to some
syntactic construct is not atomic; things can happen between each
aggregation and the eventual consumption.

The imperative version would be safer, since it could largely be made
atomic.

I like the idea in general though. I proposed (and roughly implemented)
something similar for JRuby to be able to compile to static Java
signatures, while still providing fully dynamic bodies:

class Foo
signature [String, Fixnum]
def bar(a, b)
...
end
end

This would compile to foo(RubyString a, RubyFixnum b) with appropriate
type checks and conversions, allowing us to provide a real Java method
signature. In JRuby, this would be implemented on a per-thread-basis, or
we'd use the imperative version:

signature :bar, [String, Fixnum]

- Charlie
 
K

Keith Rarick

Depending on how you've implemented it, the declarative style is not
thread-safe.

You're right; it is not. But that doesn't bother me too much.
Shared-state concurrency is usually a bad idea. Then again, ideally
this library would be correct under all circumstances including
multiple threads.

The real problem is that ruby provides no way to intercept method
definitions as they occur. The best you can do is notice that they
happen after the fact.

The best interface would be an add_method method that one could
override. The interpreter has an add_method function in C, but it's
not exposed to ruby code.

kr
 
K

Keith Rarick

Nodewrap lets you do this, but it's not safe:

That's just what I was looking for. I don't understand why this isn't
built in to ruby. I would use this feature if it didn't add an
external dependency.

kr
 
N

Nobuyoshi Nakada

Hi,

At Wed, 5 Sep 2007 07:21:41 +0900,
Keith Rarick wrote in [ruby-talk:267626]:
That's just what I was looking for. I don't understand why this isn't
built in to ruby. I would use this feature if it didn't add an
external dependency.

Because it's too dangerous rather than unsafe.
 
N

Nobuyoshi Nakada

Hi,

At Wed, 5 Sep 2007 09:31:12 +0900,
Nobuyoshi Nakada wrote in [ruby-talk:267662]:
Because it's too dangerous rather than unsafe.

...and you have to know too deep implementation details very
well, to use it.
 
R

Rick DeNatale

One notable difference between the public, private, protected notation
and these decorators is that these must appear immediately before each
method they should apply to. A decorator's effects don't stick around
beyond the very next method, so there's no danger of having it go
unnoticed further down the file.

Hmmmm, I'm just catching up after being too busy for a few weeks to
drink from the firehose that is ruby-talk.

I just wrote this yesterday:
http://talklikeaduck.denhaven2.com/articles/2007/09/04/block-your-privates

After noticing that some folks like ot artifically indent methods
definitions after private and its friends, it occurred to me that it
might be nice if you could write

class Foo
private do
def method1
end

def method2
end
end
...
end

I ran into similar issues in trying to metahack an implementation of
this idea, although I did find a way to accomplish the same effect
albeit with somewhat less pretty code, see the article for the
details.
 
P

Paul Brannan

That's just what I was looking for. I don't understand why this isn't
built in to ruby. I would use this feature if it didn't add an
external dependency.

Consider: what would happen if I moved #close from the IO class to the
Array class?

Paul
 
K

Keith Rarick

Consider: what would happen if I moved #close from the IO class to the
Array class?

There are various reasonable ways to handle errors such as this.

At first, I would expect the same behavior that would result from
taking the ruby source code for the #close method and pasting it in to
the Array class. If #close isn't written in ruby, then don't allow it
to be moved.

kr
 
K

Keith Rarick

what about http://blog.ntecs.de/articles/2004/08/16/implementing-python-decorators-in-ruby
? This seem a very useful feature to have in ruby 2.0

Interesting. As long as we're willing to modify the ruby interpreter,
why not just implement real decorators? There are some problems with
my interface (and the one described on ntecs.de) that I was willing to
live with in a pure ruby implementation. First, it's not very DRY as
each decorator needs logic to lookup and replace the method and return
its name. It's also a little fragile. Most importantly, though,
replacing the method *after* it's been defined isn't as clean as
wrapping the method object right before it gets bound to a name. Any
method_added hooks will, unfortunately, be called twice.

I'm not personally going to try getting a ruby patch accepted.

kr
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top