[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

surprising: class A; end; A === A ==> false

Sam Roberts

2/23/2005 4:25:00 PM

I'm used to thinking of === being MORE useful
On thinking about it, I can see why: in A === A, A is of class Class,
and that class is not derived from the class A, so comparison is
false...

But this causes me some trouble, because there is no way to use
case statements with a class:

class A
Ordinal = 1
end
class B
Ordinal = 2
end

t = A

case t
when A then ...
when B then ...
end

No case will ever match!

Is my only way:

if t == A
...
elsif t == B
...
elsif
.....

or is there some clever workaround?

I thought of

t = A

case t.new
when A then ...


But in my case, A and B actually have initialize methods, and they
require args (different args).


Thanks,
Sam

--
Sam Roberts <sroberts@certicom.com>


11 Answers

Jamis Buck

2/23/2005 4:59:00 PM

0

On 01:24 Thu 24 Feb , Sam Roberts wrote:
> I'm used to thinking of === being MORE useful
> On thinking about it, I can see why: in A === A, A is of class Class,
> and that class is not derived from the class A, so comparison is
> false...
>
> But this causes me some trouble, because there is no way to use
> case statements with a class:
>
> class A
> Ordinal = 1
> end
> class B
> Ordinal = 2
> end
>
> t = A
>
> case t
> when A then ...
> when B then ...
> end
>
> No case will ever match!
>
> Is my only way:
>
> if t == A
> ...
> elsif t == B
> ...
> elsif
> .....
>
> or is there some clever workaround?

You could do:

case t.name
when "A" then ...
when "B" then ...
end

or

case t.id
when A.id then ...
when B.id then ...
end

Still kind of kludgy, though.

- Jamis

--
Jamis Buck
jamis_buck@byu.edu
http://jamis.jam...
------------------------------
"I am Victor of Borge. You will be assimil-nine-ed."



Robert Klemme

2/23/2005 5:00:00 PM

0


"Sam Roberts" <sroberts@uniserve.com> schrieb im Newsbeitrag
news:20050223162406.GC11408@certicom.com...
> I'm used to thinking of === being MORE useful
> On thinking about it, I can see why: in A === A, A is of class Class,
> and that class is not derived from the class A, so comparison is
> false...
>
> But this causes me some trouble, because there is no way to use
> case statements with a class:
>
> class A
> Ordinal = 1
> end
> class B
> Ordinal = 2
> end
>
> t = A
>
> case t
> when A then ...
> when B then ...
> end
>
> No case will ever match!
>
> Is my only way:
>
> if t == A
> ...
> elsif t == B
> ...
> elsif
> .....
>
> or is there some clever workaround?
>
> I thought of
>
> t = A
>
> case t.new
> when A then ...
>
>
> But in my case, A and B actually have initialize methods, and they
> require args (different args).

Nah, this is better (define a criterion):

>> class A;end
=> nil
>> classA = Object.new
=> #<Object:0x1018a900>
>> def classA.===(x) x == A end
=> nil
>> case A
>> when classA
>> puts "yes"
>> end
yes
=> nil

Of course you could encapsulate all this in a class.

Kind regards

robert

Jim Weirich

2/23/2005 5:39:00 PM

0


Jamis Buck said:
> On 01:24 Thu 24 Feb , Sam Roberts wrote:
>> Is my only way:
[... code elided ...]
>> or is there some clever workaround?
>
> You could do:
>
> case t.name
> when "A" then ...
> when "B" then ...
> end
>
> or
>
> case t.id
> when A.id then ...
> when B.id then ...
> end
>
> Still kind of kludgy, though.

You could do ...

class A
def do_something() ... end
end
class B
def do_something() ... end
end

t.do_something

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)



Brian Schröder

2/23/2005 5:45:00 PM

0

On Thu, 24 Feb 2005 02:38:35 +0900, Jim Weirich <jim@weirichhouse.org> wrote:
>
> Jamis Buck said:
> > On 01:24 Thu 24 Feb , Sam Roberts wrote:
> >> Is my only way:
> [... code elided ...]
> >> or is there some clever workaround?
> >
> > You could do:
> >
> > case t.name
> > when "A" then ...
> > when "B" then ...
> > end
> >
> > or
> >
> > case t.id
> > when A.id then ...
> > when B.id then ...
> > end
> >
> > Still kind of kludgy, though.
>
> You could do ...
>
> class A
> def do_something() ... end
> end
> class B
> def do_something() ... end
> end
>
> t.do_something
>
Surprising, that the ruby way comes up at last. Quack ;)

--
Brian Schröder
http://ruby.brian-sch...



Brian Schröder

2/23/2005 5:48:00 PM

0

On Wed, 23 Feb 2005 18:44:20 +0100, Brian Schröder <ruby.brian@gmail.com> wrote:
> On Thu, 24 Feb 2005 02:38:35 +0900, Jim Weirich <jim@weirichhouse.org> wrote:
> >
> > Jamis Buck said:
> > > On 01:24 Thu 24 Feb , Sam Roberts wrote:
> > >> Is my only way:
> > [... code elided ...]
> > >> or is there some clever workaround?
> > >
> > > You could do:
> > >
> > > case t.name
> > > when "A" then ...
> > > when "B" then ...
> > > end
> > >
> > > or
> > >
> > > case t.id
> > > when A.id then ...
> > > when B.id then ...
> > > end
> > >
> > > Still kind of kludgy, though.
> >
> > You could do ...
> >
> > class A
> > def do_something() ... end
> > end
> > class B
> > def do_something() ... end
> > end
> >
> > t.do_something
> >
> Surprising, that the ruby way comes up at last. Quack ;)

The above shall read: came up in the last message. I'm not shure if I
haven't said something completely different. Sorry that I have to
follow up my superfluous no-advice-message with another one correcting
it.

Think first, then type then think then send!

Regards,

Brian

--
Brian Schröder
http://ruby.brian-sch...



Sam Roberts

2/24/2005 2:58:00 AM

0

Quoting jim@weirichhouse.org, on Thu, Feb 24, 2005 at 02:38:35AM +0900:
>
> Jamis Buck said:
> > On 01:24 Thu 24 Feb , Sam Roberts wrote:
> >> Is my only way:
> [... code elided ...]
> > case t.id
> > when A.id then ...
> > when B.id then ...
> > end
> >
> > Still kind of kludgy, though.

I think thats the best suggestion so far.

> You could do ...
>
> class A
> def do_something() ... end
> end
> class B
> def do_something() ... end
> end
>
> t.do_something

I know where you are coming from, this is a nice pattern, but in this
case t is a return value of Resolv::DNS::Message#question, and I have to
answer the question. How I answer the question depends on the question
(of course!), but is not part of the behaviour of the question,
different "answers" answer the question in different ways.

Thanks for all the suggestions, folks.

Sam



Bertram Scharpf

2/24/2005 3:39:00 AM

0

Hi,

Am Donnerstag, 24. Feb 2005, 11:58:09 +0900 schrieb Sam Roberts:
> Quoting jim@weirichhouse.org, on Thu, Feb 24, 2005 at 02:38:35AM +0900:
> > You could do ...
> >
> > class A
> > def do_something() ... end
> > end
> > class B
> > def do_something() ... end
> > end
> >
> > t.do_something

Of course.

> I know where you are coming from, this is a nice pattern, but in this
> case t is a return value of Resolv::DNS::Message#question, and I have to
> answer the question. How I answer the question depends on the question
> (of course!), but is not part of the behaviour of the question,
> different "answers" answer the question in different ways.

I accept that. So I try:

$ ruby -e 'class Class ; def === oth ; self == oth ; end ; end ;
> class C ; end ; p C === C'
true

But this one crashes:

$ irb
irb(main):001:0> class Class ; def === oth ; self == oth ; end ; end
=> nil
irb(main):002:0> class C ; end
/usr/local/lib/ruby/1.9/irb/ruby-token.rb:101:in `Token': undefined method `ancestors' for ";":String (NoMethodError)
...

Is this the behaviour to be expected?

Bertram


--
Bertram Scharpf
Stuttgart, Deutschland/Germany
http://www.bertram-...


Martin DeMello

2/24/2005 4:13:00 AM

0

Bertram Scharpf <lists@bertram-scharpf.de> wrote:
>
> $ ruby -e 'class Class ; def === oth ; self == oth ; end ; end ;
> > class C ; end ; p C === C'
> true

Not sure about the crash, but this is probably more useful behaviour:

class Class
alias :=== :caseeq

def ===(other)
(self == other) || self.caseeq(other)
end
end

martin

Jim Weirich

2/24/2005 6:19:00 AM

0

On Wednesday 23 February 2005 09:58 pm, Sam Roberts wrote:
> > You could do ...
> >
> > class A
> > def do_something() ... end
> > end
> > class B
> > def do_something() ... end
> > end
> >
> > t.do_something
>
> I know where you are coming from, this is a nice pattern, but in this
> case t is a return value of Resolv::DNS::Message#question, and I have to
> answer the question. How I answer the question depends on the question
> (of course!), but is not part of the behaviour of the question,
> different "answers" answer the question in different ways.

I'm not sure if you are saying that won't work because (1) the do_something
method is not part of the behavior of the classes in question, or (2) you
need to respond differently to these classes in different circumstances. Or
perhaps both (1) and (2) are the case.

If (1), then remember that you can always open up any class and add more
behavior.

If (2), then you can do something like the following (simple visitor pattern):

module Kernel
def accept_visitor(visitor)
visitor.send("visit_" + self.class.name.downcase.gsub(/::/, '_'), self)
end
end

class A; end
class B; end

class MySpecialVisitor
def visit_a(a)
puts "Doing something with A: (#{a})"
end
def visit_b(b)
puts "Doing something else with B: (#{b})"
end
end

A.new.accept_visitor(MySpecialVisitor.new)
B.new.accept_visitor(MySpecialVisitor.new)

You can define as many visitors as you need to get the varied behaviors
required by your problem.

The solution is a bit more complicated, but allows open-ended behaviors for
any class.

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)


Sam Roberts

2/25/2005 3:50:00 AM

0

Quoting jim@weirichhouse.org, on Thu, Feb 24, 2005 at 03:18:41PM +0900:
> On Wednesday 23 February 2005 09:58 pm, Sam Roberts wrote:
> > > You could do ...
> > >
> > > class A
> > > def do_something() ... end
> > > end
> > > class B
> > > def do_something() ... end
> > > end
> > >
> > > t.do_something
> >
> > I know where you are coming from, this is a nice pattern, but in this
> > case t is a return value of Resolv::DNS::Message#question, and I have to
> > answer the question. How I answer the question depends on the question
> > (of course!), but is not part of the behaviour of the question,
> > different "answers" answer the question in different ways.
>
> I'm not sure if you are saying that won't work because (1) the do_something
> method is not part of the behavior of the classes in question, or (2) you
> need to respond differently to these classes in different circumstances. Or
> perhaps both (1) and (2) are the case.
>
> If (1), then remember that you can always open up any class and add more
> behavior.

Isn't part, and *shouldn't* be part.

> If (2), then you can do something like the following (simple visitor pattern):

Nice trick, but all it has the opposite effect (decreased code
maintainability) than using a case, in my case.

The reason you suggest this, I assume, is that switching on input type
often means that you've put logic in the wrong place, often the switch
ends up being in many places, and when you add a new input type you have
to find all the 'handling' code, and upgrade it.

However, I don't have this problem, I'm actually using a visitor pattern
already, and I've encapsulated knowledge in what (I think) is the right
place.

I'm working on a mDNS responder, and this part looks like:

question_msg = DNS::Message.decode(@sock.recv)

answer_msg = DNS::Message.new

question_msg.each_question do |question|
@services.each do |service|
service.answer_question(question, answer_msg)
end
end

@sock.send(answer_msg.encode)


My loop doesn't know what kinds of questions there are, doesn't know
what kind of answers there are, or how the questions are to be answered.

Each service has the opportunity to add an answer to the outgoing
message, if it wants to.

It's in the Service class that I need the case:

class Service

def answer_question(question, msg)
case question.type
when DNS::Resource::PTR
# ... I know an answer to that..

...

end
end
end


Adding yet another class (MySpecialVisitor) would mean I'm removing the
knowledge from the Service of what questions it can answer, it would
become a dumb container with getter/setter types functions, and this new
class would have all the knowledge. It would be worth it only if there
were multiple ways to answer questions for a given service... but
there's not.

Thanks for the very interesting code snippet, I may indeed use that
approach one of these days!

Cheers,
Sam