making a duck

R

Robert Klemme

Eric said:
because you can get a handle on that rebound Proc. You might
want to pass it around or whatever.

I see. Although I don't have a use case for this at hand and in fact
never missed that. But that might be just my personal experience.
When do you think will unbind_locals be useful?

As a replacement for many string evals - which are ugly,
inefficient, and possibly dangerous. Many (most?) times that
you need to eval a string it is because you need to pull in
some local variables to help define the string to be evaled.
Here is the first example of a string eval in the 1.8 library I
found:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
LI OPTION tr th td ]
methods += <<-BEGIN + nO_element_def(element) + <<-END
def #{element.downcase}(attributes = {})
BEGIN
end
END
end
eval(methods)

This could be replaced by:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
LI OPTION tr th td ]
define_method(element.downcase.to_sym ,
proc { |attributes={}|
nO_element_def(element)
}.unbind_locals # replace element with constant
)
end

Much cleaner, huh?

Not really (at least to my eyes). Also, there are some issues:

- I don't know what nO_element_def does exactly, but it will have to be
rewritten to not create a string with ruby code

- Your version might be less efficient.

- There might be subtle differences because "element" is pulled into the
closure

- Also, unbind_locals will remove "element" from the procs bindings
Transferring locals might be pretty easy, but transferring the
meaning of self would be more difficult, I think. At least
without making a new Binding (and then you'd still need a way
to rebind it to the original proc).

Why do you think that self is special? If there is a general mechanism to
transfer state (i.e. bindings) into a binding, any variable can be
rebound. I imagine something like

proc.binding.bind:)self => whatever, :foo => "bar")
eval("self", proc.binding) # -> whatever
eval("foo", proc.binding) # -> "bar"

Interesting. I assumed that since
class_eval/instance_eval/module_eval all returned the same
"self" that they did the same thing. The only difference I see
is in "def <method> ...". With class_eval, it defines instance
methods and with instance_eval, it defines class methods.
That's kind of strange. Some kind of magic is going on here
other than the changing of self.
Definitely.

I was hoping that instance_eval could be used to define class
methods using #define_method, but #define_method does the same
with both - defines instance methods. Anybody know of an
equivalent to #define_method for making class methods? I
couldn't figure out any way to define a class method from a
proc - just out of curiosity.

Since a class method is just an instance method of the class:
?> Foo.bar
=> "bar"
Thanks for the interesting exchange!

Kind regards

robert
 
E

Eric Mahurin

--- Robert Klemme said:
When do you think will unbind_locals be useful?

As a replacement for many string evals - which are ugly,
inefficient, and possibly dangerous. Many (most?) times that
you need to eval a string it is because you need to pull in
some local variables to help define the string to be evaled.
Here is the first example of a string eval in the 1.8 library I
found:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
LI OPTION tr th td ]
methods += <<-BEGIN + nO_element_def(element) + <<-END
def #{element.downcase}(attributes = {})
BEGIN
end
END
end
eval(methods)

This could be replaced by:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
LI OPTION tr th td ]
define_method(element.downcase.to_sym ,
proc { |attributes={}|
nO_element_def(element,attributes)
}.unbind_locals # replace element with constant
)
end

Much cleaner, huh?

Not really (at least to my eyes). Also, there are some
issues:

- I don't know what nO_element_def does exactly, but it will
have to be
rewritten to not create a string with ruby code

Yep. I wasn't paying attention very well. Here is its
definition (this is from cgi.rb, BTW):

def nOE_element_def(element, append = nil)
s = <<-END
"<#{element.upcase}" + attributes.collect{|name, value|
next unless value
" " + CGI::escapeHTML(name) +
if true == value
""
else
'="' + CGI::escapeHTML(value) + '"'
end
}.to_s + ">"
END
s.sub!(/\Z/, " +") << append if append
s
end
def nO_element_def(element)
nOE_element_def(element, <<-END)
if block_given?
yield.to_s + "</#{element.upcase}>"
else
""
end
END
end

Here would be the evaluating (instead of generating) version:

def nOE_element_def(element,attributes)
"<#{element.upcase}" + attributes.collect{|name, value|
next unless value
" " + CGI::escapeHTML(name) +
if true == value
""
else
'="' + CGI::escapeHTML(value) + '"'
end
}.to_s + ">"
end
def nO_element_def(element,attributes)
nOE_element_def(element,attributes) +
if block_given?
yield.to_s + "<#{element.upcase}>"
else
""
end
end
- Your version might be less efficient.

If you flatten the hierarchy, you should be able to get the
same efficiency:

for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD
LI OPTION tr th td ]
define_method(element.downcase.to_sym ,
proc { |attributes={}|
"<#{element.upcase}" + attributes.collect{|name, value|
next unless value
" " + CGI::escapeHTML(name) +
if true == value
""
else
'="' + CGI::escapeHTML(value) + '"'
end
}.to_s + ">" +
if block_given?
yield.to_s + "<#{element.upcase}>"
else
""
end
}.unbind_locals # replace element with constant
)
end
- There might be subtle differences because "element" is
pulled into the
closure

- Also, unbind_locals will remove "element" from the procs
bindings

I'm assuming that unbind_locals will simply replace any local
variables (external to the proc) with what their current value
is: element will become "HTML", "HEAD", "BODY", etc.
Why do you think that self is special? If there is a general
mechanism to
transfer state (i.e. bindings) into a binding, any variable
can be
rebound. I imagine something like

proc.binding.bind:)self => whatever, :foo => "bar")
eval("self", proc.binding) # -> whatever
eval("foo", proc.binding) # -> "bar"

I was assuming that with local variables, you just be moving
the value from one binding to another - not aliasing. The
problem is that you can't assign to "self". If you really
could do the above, then this:

binding.bind:)self => whatever)

would be equivalent to:

self = whatever

I don't that would be a trivial thing to implement. But, if
this "bind" created a new binding (i.e. non-destructive instead
of destructive), it would seem more feasible:

whateverBinding = binding.bind:)self => whatever)
whateverBinding.self.object_id==whatever.object_id

But then you are back to where you started - you still have a
bind the proc to a different binding (probably returning a new
proc).
Since a class method is just an instance method of the class:

Thanks! That works, but I wanted the proc to have visibility
to the local variables. I think this would be more useful:

class Foo;end
(class<<Foo;self;end).send:)define_method,:bar) {"bar"}
Thanks for the interesting exchange!

and thanks for your ideas.


... back to my original topic - making a duck. For my stuff, I
think I've decided to have 2 ways of doing it:

# make a duck using methods from obj
myDuck = obj.duck(newSym, oldSym, ...)

# make a duck using arbitrary procs
myDuck = duck(methSym => methProc, ...)

One of the applications I'm looking at now is for a method that
looks like this:

Cursor#scan(value)

where value is String/Array like. All it needs is to respond
to #[int] which should return an object that responds to #==.
Although this #scan may look like it just matches to a verbatim
String/Array, you could also do something like this to match to
some digits:

# make == look like ===
digit = (?0..?9).duck:)==,:===)
# match to a string of 4 digits
# Proc responds to [] like a String/Array
cursor.scan(proc{|i|(i<4)? digit : nil})

Do people do things like this with duck-typing? Or are most
just all talk. Doing something like the above is where I see
the power.





__________________________________
Discover Yahoo!
Use Yahoo! to plan a weekend, have fun online and more. Check it out!
http://discover.yahoo.com/
 
N

nobuyoshi nakada

Hi,

At Thu, 9 Jun 2005 01:30:53 +0900,
Eric Mahurin wrote in [ruby-talk:144892]:
# make == look like ===
digit = (?0..?9).duck:)==,:===)
# match to a string of 4 digits
# Proc responds to [] like a String/Array
cursor.scan(proc{|i|(i<4)? digit : nil})

It would be called as `alias', I guess.
 
E

Eric Mahurin

--- nobuyoshi nakada said:
Hi,

At Thu, 9 Jun 2005 01:30:53 +0900,
Eric Mahurin wrote in [ruby-talk:144892]:
# make == look like ===
digit = (?0..?9).duck:)==,:===)
# match to a string of 4 digits
# Proc responds to [] like a String/Array
cursor.scan(proc{|i|(i<4)? digit : nil})

It would be called as `alias', I guess.

I don't care too much what the method names are. Below is the
implementation I was thinking. This gives you 3 ways to make a
"duck" (a minimal object for a duck-typed argument of a
method):

digit = (?0..?9).duck:)==,:include?,:to_s,:inspect)
alpha = Object.duck(size: proc{26}, :== =>
(?a..?z).method:)===) )
test = Object.duck:)hi) {puts "hello world!"}

Any chance of getting something like this in the standard lib?
Your "Behavior" implementation is fine too.


class Object
def duck(*new_old)
obj = Object.new
klass = (class<<obj;self;end)
until new_old.empty?
new,old = new_old.slice!(0,2)
klass.send:)define_method,new,&self.method(old))
end
obj
end
def self.duck(methods,&block)
obj = self.new
klass = (class<<obj;self;end)
if block
klass.send:)define_method,methods,&block)
else
methods.each { |name,proc|
klass.send:)define_method,name,&proc)
}
end
obj
end
end




__________________________________
Discover Yahoo!
Use Yahoo! to plan a weekend, have fun online and more. Check it out!
http://discover.yahoo.com/
 

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,982
Messages
2,570,185
Members
46,737
Latest member
Georgeengab

Latest Threads

Top