[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

making a duck

Eric Mahurin

6/6/2005 4:00:00 PM

Regarding duck-typing... Is there an easy way make a "duck"?
i.e. an object that responds to enough methods to be an
acceptable argument to a certain methods. For example, if I
have a method that takes aString and uses the #size and #[i]
methods, I could pass it something that looked just enough like
a String to work:

alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})

The implementation I came up with is this:

class Object
def self.duck(*name_proc)
duck = self.new
duckclass = (class << duck;self;end)
while not name_proc.empty?
name,proc = name_proc.slice!(0,2)
duckclass.send(:define_method,name,&proc)
end
duck
end
end


Is there another way to do this? With all of the talk about
duck-typing, there would already be something out there to do
this.




__________________________________
Discover Yahoo!
Stay in touch with email, IM, photo sharing and more. Check it out!
http://discover.yahoo.com/stayin...


27 Answers

Jacob Fugal

6/6/2005 4:42:00 PM

0

On 6/6/05, Eric Mahurin <eric_mahurin@yahoo.com> wrote:
> Regarding duck-typing... Is there an easy way make a "duck"?
> i.e. an object that responds to enough methods to be an
> acceptable argument to a certain methods. For example, if I
> have a method that takes aString and uses the #size and #[i]
> methods, I could pass it something that looked just enough like
> a String to work:
>
> alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
>
> The implementation I came up with is this:
>
> class Object
> def self.duck(*name_proc)
> duck = self.new
> duckclass = (class << duck;self;end)
> while not name_proc.empty?
> name,proc = name_proc.slice!(0,2)
> duckclass.send(:define_method,name,&proc)
> end
> duck
> end
> end
>
>
> Is there another way to do this? With all of the talk about
> duck-typing, there would already be something out there to do
> this.

Well, my way isn't really different, and I actually wouldn't use
either "my" way or yours, but in the spirit of ruby I propose this
revision:

class Object
def self.duck(methods = {})
duck = self.new
duckclass = (class << duck; self; end)
methods.each do |name,proc|
duckclass.send(:define_method, name, &proc)
end
duck
end
end

alphabet = Object.duck(
:[] => proc{ |i| ?a+i },
:size => proc{ 26 }
)

I simply replaced the array with a hash. Looks a bit cleaner to me.

Jacob Fugal


gabriele renzi

6/6/2005 4:51:00 PM

0

Eric Mahurin ha scritto:

> Is there another way to do this? With all of the talk about
> duck-typing, there would already be something out there to do
> this.

There was some talk in the past about Object.new taking a block, which
imho would be useful for this. Meanwhile what about using Struct.new
with a block to define methods?

dblack

6/6/2005 4:57:00 PM

0

Robert Klemme

6/6/2005 5:07:00 PM

0

Eric Mahurin wrote:
> Regarding duck-typing... Is there an easy way make a "duck"?
> i.e. an object that responds to enough methods to be an
> acceptable argument to a certain methods. For example, if I
> have a method that takes aString and uses the #size and #[i]
> methods, I could pass it something that looked just enough like
> a String to work:
>
> alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})

Why aren't you satisfied with

class Alpha
def [](i) ?a+i end
def size() 26 end
end
alphabet = Alpha.new

or

alphabet = Object.new
def alphabet.[](i) ?a+i end
def alphabet.size() 26 end

You can even squeeze that on one line if you feel the need for it. I
mean, you don't generate those methods dynamically or get them from
somewhere else so why not just use the std approach?

Kind regards

robert

Eric Mahurin

6/6/2005 5:51:00 PM

0

--- "David A. Black" <dblack@wobblini.net> wrote:

> Hi --
>
> On Tue, 7 Jun 2005, Eric Mahurin wrote:
>
> > Regarding duck-typing... Is there an easy way make a
> "duck"?
> > i.e. an object that responds to enough methods to be an
> > acceptable argument to a certain method. For example, if
> I
> > have a method that takes aString and uses the #size and
> #[i]
> > methods, I could pass it something that looked just enough
> like
> > a String to work:
> >
> > alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
> >
> > The implementation I came up with is this:
> >
> > class Object
> > def self.duck(*name_proc)
> > duck = self.new
> > duckclass = (class << duck;self;end)
> > while not name_proc.empty?
> > name,proc = name_proc.slice!(0,2)
> > duckclass.send(:define_method,name,&proc)
> > end
> > duck
> > end
> > end
> >
> >
> > Is there another way to do this? With all of the talk
> about
> > duck-typing, it seems like there would already be something
out there to
> do
> > this.
>
> I've always understood "duck typing" to refer to a particular
> approach
> to the handling of method calls on objects, so I personally
> wouldn't
> expect a to see a correlation between the amount of talk
> about duck
> typing and this kind of technique. Your technique looks to
> me like it
> has more to do with the opposite end of the process: the
> preparation
> and priming of an object, as a way of creating a situation
> favorable
> to subsequent duck typing. I don't think what you've got
> here stands
> in any unique or special relation to duck typing; after all,
> from the
> duck typing perspective, a method that an object got from its
> original
> Class is just as much part of the object's behavior as a
> method the
> object got extended with later.

Correct. I'm looking at the other side - making an object (a
"duck") that works well with a method using duck-typing.

> In fact... your technique actually calls to mind
> Module#extend. Do
> you have a case where you'd prefer to do it the way you've
> done it
> above, rather than defining the methods in a module and then
> extending
> your object with it?

I was wanting to define the duck in-line with arbitrary procs
for the methods. I'll give you a more concrete example using a
duck-typed method. Take enum.include?(obj) for example. It
searchs in enum something that matches obj using obj==element.
Let's say I wanted to match with obj===element or
obj.include?(element), etc. I could do these:

enum.include?(Object.duck(:==,range.method(:===)))
enum.include?(Object.duck(:==,set.method(:include?)))
enum.include?(Object.duck(:==,hash.method(:[])))
enum.include?(Object.duck(:==,proc{|element|...}))

A special case that is very easily handled right now is if your
duck typing method takes an argument that only needs to respond
to []. For example:

def scan_while(aHash)
loop {
...
aHash[element] or break
...
}
end

scan_while(hash)
scan_while(proc{|element|...})
scan_while(set.method(:include?))
scan_while(range.method(:===))
scan_while(obj.method(:==))




__________________________________
Discover Yahoo!
Get on-the-go sports scores, stock quotes, news and more. Check it out!
http://discover.yahoo.com/m...


Eric Mahurin

6/6/2005 6:52:00 PM

0

--- Robert Klemme <bob.news@gmx.net> wrote:

> Eric Mahurin wrote:
> > Regarding duck-typing... Is there an easy way make a
> "duck"?
> > i.e. an object that responds to enough methods to be an
> > acceptable argument to a certain methods. For example, if
> I
> > have a method that takes aString and uses the #size and
> #[i]
> > methods, I could pass it something that looked just enough
> like
> > a String to work:
> >
> > alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
>
> Why aren't you satisfied with
>
> class Alpha
> def [](i) ?a+i end
> def size() 26 end
> end
> alphabet = Alpha.new
>
> or
>
> alphabet = Object.new
> def alphabet.[](i) ?a+i end
> def alphabet.size() 26 end
>
> You can even squeeze that on one line if you feel the need
> for it. I
> mean, you don't generate those methods dynamically or get
> them from
> somewhere else so why not just use the std approach?

For the example I gave above, I think you are correct. The
examples I gave in response to David Black are probably better
ones. With those, a simple "def" won't cut it. You need
define_method. But, using define_method is cumbersome from an
object because you first need to make it have a singleton
class, then use "send" to access it from that class because it
it a private method. Another solution to the problem of
"making a duck" would be to have a
Object#define_singleton_method:

class Object
def define_singleton_method(name,&block)
klass = (class << self;self;end)
klass.send(:define_method,name,&block)
self
end
end

Then, for example, you could do this to make a set be useful
for a method that uses == for comparison:

seteq = Object.new.
define_singleton_method(:==,&set.method(:include?))

I'm not sure why this isn't in Object right now. It seems like
the rest of the *singleton_method* methods are there.




__________________________________
Discover Yahoo!
Find restaurants, movies, travel and more fun for the weekend. Check it out!
http://discover.yahoo.com/we...



nobu.nokada

6/7/2005 1:40:00 AM

0

Hi,

At Tue, 7 Jun 2005 01:00:23 +0900,
Eric Mahurin wrote in [ruby-talk:144691]:
> Regarding duck-typing... Is there an easy way make a "duck"?
> i.e. an object that responds to enough methods to be an
> acceptable argument to a certain methods. For example, if I
> have a method that takes aString and uses the #size and #[i]
> methods, I could pass it something that looked just enough like
> a String to work:
>
> alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})

I'd posted a feature called `behavior' in [ruby-dev:25772].

alphabet = Object.behaving(:[]) {|i|(?a+i).chr}
p alphabet[20] #=> "u"

--
Nobu Nakada


Eric Mahurin

6/7/2005 3:00:00 AM

0

--- nobu.nokada@softhome.net wrote:

> Hi,
>
> At Tue, 7 Jun 2005 01:00:23 +0900,
> Eric Mahurin wrote in [ruby-talk:144691]:
> > Regarding duck-typing... Is there an easy way make a
> "duck"?
> > i.e. an object that responds to enough methods to be an
> > acceptable argument to a certain methods. For example, if
> I
> > have a method that takes aString and uses the #size and
> #[i]
> > methods, I could pass it something that looked just enough
> like
> > a String to work:
> >
> > alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
>
> I'd posted a feature called `behavior' in [ruby-dev:25772].
>
> alphabet = Object.behaving(:[]) {|i|(?a+i).chr}
> p alphabet[20] #=> "u"
>
> --
> Nobu Nakada

Looks like 2 approaches to doing the same thing. I do like the
hash interface a little better. From your code that I read, it
looks like you can do this:

alphabet = Object.new.behaving(:[] => proc{|i|?a+i}, size:
proc{26})

I didn't know about the ":symbol => value" shortcut of "symbol:
value". Or forgot about it. Very nice in this situation.

Is there an advantage to having a separate Behavior class as
opposed the solution I had: making a singleton Object directly?

I think having something like this readily available would
promote more (interesting) uses of duck-typing.




__________________________________
Discover Yahoo!
Stay in touch with email, IM, photo sharing and more. Check it out!
http://discover.yahoo.com/stayin...


nobu.nokada

6/7/2005 7:49:00 AM

0

Hi,

At Tue, 7 Jun 2005 11:59:47 +0900,
Eric Mahurin wrote in [ruby-talk:144750]:
> Is there an advantage to having a separate Behavior class as
> opposed the solution I had: making a singleton Object directly?

To allow sharing same behavior.

--
Nobu Nakada


Robert Klemme

6/7/2005 7:51:00 AM

0

Eric Mahurin wrote:
> --- Robert Klemme <bob.news@gmx.net> wrote:
>
>> Eric Mahurin wrote:
>>> Regarding duck-typing... Is there an easy way make a
>> "duck"?
>>> i.e. an object that responds to enough methods to be an
>>> acceptable argument to a certain methods. For example, if
>> I
>>> have a method that takes aString and uses the #size and
>> #[i]
>>> methods, I could pass it something that looked just enough
>> like
>>> a String to work:
>>>
>>> alphabet = Object.duck(:[],proc{|i|?a+i},:size,proc{26})
>>
>> Why aren't you satisfied with
>>
>> class Alpha
>> def [](i) ?a+i end
>> def size() 26 end
>> end
>> alphabet = Alpha.new
>>
>> or
>>
>> alphabet = Object.new
>> def alphabet.[](i) ?a+i end
>> def alphabet.size() 26 end
>>
>> You can even squeeze that on one line if you feel the need
>> for it. I
>> mean, you don't generate those methods dynamically or get
>> them from
>> somewhere else so why not just use the std approach?
>
> For the example I gave above, I think you are correct. The
> examples I gave in response to David Black are probably better
> ones. With those, a simple "def" won't cut it. You need
> define_method. But, using define_method is cumbersome from an
> object because you first need to make it have a singleton
> class, then use "send" to access it from that class because it
> it a private method. Another solution to the problem of
> "making a duck" would be to have a
> Object#define_singleton_method:
>
> class Object
> def define_singleton_method(name,&block)
> klass = (class << self;self;end)
> klass.send(:define_method,name,&block)
> self
> end
> end

Btw, you can also use class eval:

class Object
def define_singleton_method(name,&block)
klass = (class << self;self;end).class_eval do
define_method(name,&block)
end
self
end
end

and also

o=Object.new
class <<o;self;end.class_eval do
def bax() "bar" end
end
o.bax

> Then, for example, you could do this to make a set be useful
> for a method that uses == for comparison:
>
> seteq = Object.new.
> define_singleton_method(:==,&set.method(:include?))

This does not work. You cannot transfer a method from one class to
another:

09:14:01 [ruby]: cat define.rb

class Foo
def test() bar() end
def bar() "FOO::BAR" end
end

class Bar
def bar() "BAR::BAR" end
end

bar = Bar.new
bar_kl = (class<<bar;self;end)
bar_kl.send(:define_method, :xxx, Foo.new.method(:test))
bar_kl.send(:public, :xxx)

bar.xxx()
09:15:33 [ruby]: ruby define.rb
define.rb:16:in `xxx': bind argument must be an instance of Foo
(TypeError)
from define.rb:16


> I'm not sure why this isn't in Object right now. It seems like
> the rest of the *singleton_method* methods are there.

Probably because it doesn't work - at least not completely the way you
like to have it. Btw, and if you're borrowing implementations from other
classes then you can directly use an instance of that class...

Kind regards

robert