[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Symbols are your friends

Daniel Schierbeck

7/27/2006 10:47:00 AM

Yes, it's time for another Symbol trick!

class Symbol
def ===(obj)
obj.respond_to? "to_#{self}"
end
end

case "foo"
when :int then "integer"
when :str then "string"
when :ary then "array"
end #=> "string"

Genius. Sheer genius.


Daniel
8 Answers

Caleb Clausen

7/27/2006 4:50:00 PM

0

On 7/27/06, Daniel Schierbeck <daniel.schierbeck@gmail.com> wrote:
> Yes, it's time for another Symbol trick!
>
> class Symbol
> def ===(obj)
> obj.respond_to? "to_#{self}"
> end
> end

This idea has been discussed on this list before; I can't find the
thread at the moment.
This seems like a really clever idea, and is fine for just playing
around... but it's not advisable for general use. You're changing the
semantics of Symbol#===, which will break things like this:

case method_name
when :reverse
#....
when :each
#...
else #.....
end

The following is a simplified extract of what I use in Reg for a
similar feature. It's safer, as long as nothing else decides to do
something with Symbol#-@.

class Symbol
def -@; Reg::Knows.new(self) end
end

module Reg
class Knows
def initialize(sym)
@sym=sym
end
def ===(other)
other.respond_to? @sym
end
end
end


(You're prepending "to_" to your method names before checking; I don't
know why. This way seems more general....)

Daniel Schierbeck

7/27/2006 5:52:00 PM

0

Caleb Clausen wrote:
> On 7/27/06, Daniel Schierbeck <daniel.schierbeck@gmail.com> wrote:
>> Yes, it's time for another Symbol trick!
>>
>> class Symbol
>> def ===(obj)
>> obj.respond_to? "to_#{self}"
>> end
>> end
>
> This idea has been discussed on this list before; I can't find the
> thread at the moment.
> This seems like a really clever idea, and is fine for just playing
> around... but it's not advisable for general use. You're changing the
> semantics of Symbol#===, which will break things like this:
>
> case method_name
> when :reverse
> #....
> when :each
> #...
> else #.....
> end

Yes, that's true -- I only ever intended it to be a trick.

> (You're prepending "to_" to your method names before checking; I don't
> know why. This way seems more general....)

I was using it to demonstrate how you could duck type check with a case
statement, so focusing on the "type" made sense. I see this all the time:

case obj
when Integer then foo
when String then bar
end

and it strikes me as not being very duckish (quacky?) Using if
statements can be annoying:

if obj.respond_to? :to_str
foo
elsif obj.respond_to? :to_int
bar
end

Maybe this would work?

class Symbol
def ===(obj)
return true if self == obj
obj.respond_to? "to_#{self}"
end
end

though something like this may cause trouble:

obj = :int
case obj
when :int then "integer"
when :str then "string"
end


Cheers,
Daniel

Caleb Clausen

7/27/2006 7:11:00 PM

0

On 7/27/06, Daniel Schierbeck <daniel.schierbeck@gmail.com> wrote:
> > (You're prepending "to_" to your method names before checking; I don't
> > know why. This way seems more general....)
>
> I was using it to demonstrate how you could duck type check with a case
> statement, so focusing on the "type" made sense. I see this all the time:
>
> case obj
> when Integer then foo
> when String then bar
> end
>
> and it strikes me as not being very duckish (quacky?) Using if
> statements can be annoying:
>
> if obj.respond_to? :to_str
> foo
> elsif obj.respond_to? :to_int
> bar
> end

I see where you're coming from, but I guess I just don't see that
#to_* methods as being that special. Why wouldn't you want to do this
kind of dispatch for other methods too?

Using the code I posted previously, the above conditionals could be written as:

case obj
when -:to_str
foo
when -:to_int
bar
end

I like that one best.

Fooling with the existing Symbol#=== in any way is very likely to
break some existing libs which assumes the current definition always
obtains... my way lets you use (almost-)symbols in the way you want to
use them. It is incompatible with libs that also define Symbol#-@ in a
different way, but those are presumably much rarer, perhaps
nonexistant. (Nobody's as crazy as I am.) If you really want this
feature in a general way, (and I do, for Reg) this seems to be the
best compromise.

Daniel Schierbeck

7/27/2006 8:54:00 PM

0

Caleb Clausen wrote:
> On 7/27/06, Daniel Schierbeck <daniel.schierbeck@gmail.com> wrote:
>> > (You're prepending "to_" to your method names before checking; I don't
>> > know why. This way seems more general....)
>>
>> I was using it to demonstrate how you could duck type check with a case
>> statement, so focusing on the "type" made sense. I see this all the time:
>>
>> case obj
>> when Integer then foo
>> when String then bar
>> end
>>
>> and it strikes me as not being very duckish (quacky?) Using if
>> statements can be annoying:
>>
>> if obj.respond_to? :to_str
>> foo
>> elsif obj.respond_to? :to_int
>> bar
>> end
>
> I see where you're coming from, but I guess I just don't see that
> #to_* methods as being that special. Why wouldn't you want to do this
> kind of dispatch for other methods too?

Because I only think the #to_x methods are directly related to types.
They even have more nuances than class-based types; there's a difference
between defining #to_s and defining #to_str, for example.

> Using the code I posted previously, the above conditionals could be
> written as:
>
> case obj
> when -:to_str
> foo
> when -:to_int
> bar
> end
>
> I like that one best.
>
> Fooling with the existing Symbol#=== in any way is very likely to
> break some existing libs which assumes the current definition always
> obtains... my way lets you use (almost-)symbols in the way you want to
> use them. It is incompatible with libs that also define Symbol#-@ in a
> different way, but those are presumably much rarer, perhaps
> nonexistant. (Nobody's as crazy as I am.) If you really want this
> feature in a general way, (and I do, for Reg) this seems to be the
> best compromise.

Yes, I've adopted your solution; it is indeed the best way to get around
the problem.

This isn't something I believe should go into core or anything, I just
think it's a great showcase of Ruby's flexibility.


Cheers,
Daniel

Aleks Kissinger

7/28/2006 4:29:00 PM

0

> Because I only think the #to_x methods are directly related to types.
> They even have more nuances than class-based types; there's a difference
> between defining #to_s and defining #to_str, for example.
>

In the spirit of making sure I understand, and perhaps explaining this
to anyone else that may be confused, the difference in these two kinds
of methods:

If an object _can_ be represented as an integer in some cases (e.g. a
string, which could be set to something like "12"), it implements
to_i.

If an object _is_ a natural representation of an integer, (like roman
numerals, as the example the Pickaxe gives), it implements to_int.

So this makes sense, though nothing about the naming convention of
these methods makes this particularly clear. However, this brings up
the question: If something acts exactly like an integer method-wise,
whats the benefit of explicitly calling to_int? This seems like a non
duck-typey thing to do.


Aleks

Ara.T.Howard

7/28/2006 4:34:00 PM

0

Aleks Kissinger

7/28/2006 4:40:00 PM

0

That makes sense. Thanks.

On 7/28/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
> On Sat, 29 Jul 2006, Aleks Kissinger wrote:
>
> > So this makes sense, though nothing about the naming convention of these
> > methods makes this particularly clear. However, this brings up the
> > question: If something acts exactly like an integer method-wise, whats the
> > benefit of explicitly calling to_int? This seems like a non duck-typey
> > thing to do.
>
> - it might be faster to actually have an int
>
> - you can fail early by filtering on one 'category' of behaviours guaranteed
> by having an object respond_to? 'to_int', this is not unlike type or
> interface checking
>
> -a
> --
> we can never obtain peace in the outer world until we make peace with
> ourselves.
> - h.h. the 14th dali lama
>
>

Daniel Schierbeck

7/28/2006 6:44:00 PM

0

Aleks Kissinger wrote:
> If an object _can_ be represented as an integer in some cases (e.g. a
> string, which could be set to something like "12"), it implements
> to_i.
>
> If an object _is_ a natural representation of an integer, (like roman
> numerals, as the example the Pickaxe gives), it implements to_int.

That very nicely sums it up!

> So this makes sense, though nothing about the naming convention of
> these methods makes this particularly clear. However, this brings up
> the question: If something acts exactly like an integer method-wise,
> whats the benefit of explicitly calling to_int? This seems like a non
> duck-typey thing to do.

In the example with the roman numerals, you might not want to implement
all the behavior of Numeric or Fixnum -- it would make your class
unnecessarily complex. Rather just have a #to_int method that returned a
normal integer, be that a Fixnum or another object responded to the same
methods. In many cases, you have to return a Fixnum, or some methods
will complain.

This approach means that you can give your roman numeral objects
directly as arguments to methods. If they call #to_int on the object
before dealing with it, it'll behave just as if it's a Fixnum.

# doesn't really have to be an integer,
# but what the hell...
def double(num)
num.to_int * 2
end

double(roman_num)


Cheers,
Daniel