Class#=== has interesting results

Why does this happen?

class A
end

A == A #1 => true
A === A #2 => false

a = A.new

a === A #3 => false
a == A #4 => false
A === a #5 => true
A == a #6 => false

1 makes sense, A is an A so therefor it should be true
3 and 4 make sense since instance of A is not A and does not match A
5 and 6 make sense in that A matches a, but A is not an a.

But 2 doesn't make any sense to me, why doesn't an A match another A? Is this just something that slipped through or is there a specific reason for this?

Charles Comstock

whatisit = case obj
     when Array
        "it's an array!"
     when Hash
        "it's a hash!"
     when Septuagenarian
        "it's a septuagenarian!"
     end

$ ri Module#===
------------------------------------------------------------- Module#===
     mod === obj => true or false

···

--- Charles Comstock <cc1@cec.wustl.edu> wrote:

Why does this happen?

class A
end

A == A #1 => true
A === A #2 => false

a = A.new

a === A #3 => false
a == A #4 => false
A === a #5 => true
A == a #6 => false

1 makes sense, A is an A so therefor it should be true
3 and 4 make sense since instance of A is not A and does not match A
5 and 6 make sense in that A matches a, but A is not an a.

But 2 doesn't make any sense to me, why doesn't an A match another A?
Is this just something that slipped through or is there a specific
reason for this?

------------------------------------------------------------------------
     Case Equality---Returns +true+ if _anObject_ is an instance of
     _mod_ or one of _mod_'s descendents. Of limited use for modules,
     but can be used in +case+ statements to classify objects by class.

__________________________________
Do you Yahoo!?
Yahoo! Mail - Helps protect you from nasty viruses.
http://promotions.yahoo.com/new_mail

A is not an instance of A; see the ri info for Module#===.

···

On Wed, Jul 07, 2004 at 04:22:36PM +0900, Charles Comstock wrote:

Why does this happen?

class A
end

A == A #1 => true
A === A #2 => false

But 2 doesn't make any sense to me, why doesn't an A match another A?
Is this just something that slipped through or is there a specific
reason for this?

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

- long f_ffree; /* free file nodes in fs */
+ long f_ffree; /* freie Dateiknoten im Dateisystem */
  -- Seen in a translation

whatisit = case obj
     when Array
        "it's an array!"
     when Hash
        "it's a hash!"
     when Septuagenarian
        "it's a septuagenarian!"
     end

$ ri Module#===
------------------------------------------------------------- Module#===
     mod === obj => true or false
------------------------------------------------------------------------
     Case Equality---Returns +true+ if _anObject_ is an instance of
     _mod_ or one of _mod_'s descendents. Of limited use for modules,
     but can be used in +case+ statements to classify objects by class.

Right I was trying to use it in an example almost exactly like that except that obj was a name of a commandclass.

Ie:
   def parse(str)
     input = str.chomp.split
     comm = CommandFactory[input[0]]

     case comm
     when StartCommand
       comm.new(input[1],input[2..-1])
       @mobile = input[1]
     when HelpCommand
     else
       comm.new(@mobile,input[1..-1])
     end
   end

The idea was for a command factory to return the name of the command, and instantiate that command with the rest of the string as parameters, except for a few special case examples which would be checked against in a case statement. Since StartCommand === StartCommand returns false this doesn't work. Why you would want == to return true and === to return false in this case is beyond me. I knew about the example you gave above, that was what I was trying to emulate except at a step before.

Charles Comstock

Mauricio Fernández wrote:

···

On Wed, Jul 07, 2004 at 04:22:36PM +0900, Charles Comstock wrote:

Why does this happen?

class A
end

A == A #1 => true
A === A #2 => false

But 2 doesn't make any sense to me, why doesn't an A match another A? Is this just something that slipped through or is there a specific reason for this?

A is not an instance of A; see the ri info for Module#===.

At what point does it cause trouble for it to also return true if the class matches? Can someone give me an example of why it must match an instance and couldn't match the exact class? I understand it's only matching instances of the class, I just don't follow the logic behind why this is the default. Is there example code that uses this assumption?

Charles Comstock

I'm not sure what you mean. StartCommand is not and instance of
StartCommand or one of its decendants, therefore

StartCommand === StartCommand

returns false. Did you want the case statement behave differently
different when given a class?

Does this help:

      case comm.to_s.intern
      when :StartCommand
        comm.new(input[1],input[2..-1])
        @mobile = input[1]
      when :HelpCommand
      else
        comm.new(@mobile,input[1..-1])
      end

···

--- Charles Comstock <cc1@cec.wustl.edu> wrote:

>
> whatisit = case obj
> when Array
> "it's an array!"
> when Hash
> "it's a hash!"
> when Septuagenarian
> "it's a septuagenarian!"
> end
>
> $ ri Module#===
> ------------------------------------------------------------- Module#===
> mod === obj => true or false
> ------------------------------------------------------------------------
> Case Equality---Returns +true+ if _anObject_ is an instance of
> _mod_ or one of _mod_'s descendents. Of limited use for modules,
> but can be used in +case+ statements to classify objects by class.
>

Right I was trying to use it in an example almost exactly like that
except that obj was a name of a commandclass.

Ie:
   def parse(str)
     input = str.chomp.split
     comm = CommandFactory[input[0]]

     case comm
     when StartCommand
       comm.new(input[1],input[2..-1])
       @mobile = input[1]
     when HelpCommand
     else
       comm.new(@mobile,input[1..-1])
     end
   end

The idea was for a command factory to return the name of the command,
and instantiate that command with the rest of the string as parameters,
except for a few special case examples which would be checked against in
a case statement. Since StartCommand === StartCommand returns false
this doesn't work. Why you would want == to return true and === to
return false in this case is beyond me. I knew about the example you
gave above, that was what I was trying to emulate except at a step before.

__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - 100MB free storage!
http://promotions.yahoo.com/new_mail

Hi,

At Wed, 7 Jul 2004 17:17:36 +0900,
Charles Comstock wrote in [ruby-talk:105460]:

Ie:
   def parse(str)
     input = str.chomp.split
     comm = CommandFactory[input[0]]

       case
       when comm == StartCommand

       comm.new(input[1],input[2..-1])
       @mobile = input[1]

       when comm == HelpCommand

     else
       comm.new(@mobile,input[1..-1])
     end
   end

You just stand on a wrong assumption, case/when/=== don't work
as you imagine.

···

--
Nobu Nakada

Class === Array

-austin

···

On Thu, 8 Jul 2004 02:32:36 +0900, Charles Comstock <cc1@cec.wustl.edu> wrote:

At what point does it cause trouble for it to also return true if the
class matches? Can someone give me an example of why it must match an
instance and couldn't match the exact class? I understand it's only
matching instances of the class, I just don't follow the logic behind
why this is the default. Is there example code that uses this assumption?

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Hi --

···

On Thu, 8 Jul 2004, Charles Comstock wrote:

Mauricio Fernández wrote:

> On Wed, Jul 07, 2004 at 04:22:36PM +0900, Charles Comstock wrote:
>
>>Why does this happen?
>>
>>class A
>>end
>>
>>A == A #1 => true
>>A === A #2 => false
>>
>>But 2 doesn't make any sense to me, why doesn't an A match another A?
>>Is this just something that slipped through or is there a specific
>>reason for this?
>
>
> A is not an instance of A; see the ri info for Module#===.
>

At what point does it cause trouble for it to also return true if the
class matches?

At the point where you call it :slight_smile: It can't do both; there would be no
way to interpret the result:

  case s
    when String ... # what do you do here? print it? instantiate it?

David

--
David A. Black
dblack@wobblini.net

Which works because array is a descendent of class. I understand that. I'm
getting the impression that people aren't following why this doesn't make sense
to me so perhaps I can clarify:

------------------------------------------------------------- Module#===
     mod === obj => true or false

···

On Thu, 8 Jul 2004, Austin Ziegler wrote:

On Thu, 8 Jul 2004 02:32:36 +0900, Charles Comstock <cc1@cec.wustl.edu> wrote:
> At what point does it cause trouble for it to also return true if the
> class matches? Can someone give me an example of why it must match an
> instance and couldn't match the exact class? I understand it's only
> matching instances of the class, I just don't follow the logic behind
> why this is the default. Is there example code that uses this assumption?

Class === Array

------------------------------------------------------------------------
     Case Equality---Returns +true+ if _anObject_ is an instance of
     _mod_ or one of _mod_'s descendents. Of limited use for modules,
     but can be used in +case+ statements to classify objects by class.

Why is that this is defined like that and not like this:

  Case Equality --- Returns true if anObject is an instance of mod, one of mod's
descendents or _is mod_.

I understand what it matches, that is not the purpose of my line of questions, I
am asking why it doesn't also match itself, and for what reason you would want
to match descendents of x but not x itself. Does anyone have a logical reason
why this is the case?

Charles Comstock

Sorry perhaps I didn't word my question well. I meant when does it cause a
problem for a class x to match itself? What code fails if A === A results in
true? Why would you ever want to match the descendents and instances of A but
not match A itself.

Charles Comstock

···

On Thu, 8 Jul 2004, David A. Black wrote:

Hi --

On Thu, 8 Jul 2004, Charles Comstock wrote:

> Mauricio Fernández wrote:
>
> > On Wed, Jul 07, 2004 at 04:22:36PM +0900, Charles Comstock wrote:
> >
> >>Why does this happen?
> >>
> >>class A
> >>end
> >>
> >>A == A #1 => true
> >>A === A #2 => false
> >>
> >>But 2 doesn't make any sense to me, why doesn't an A match another A?
> >>Is this just something that slipped through or is there a specific
> >>reason for this?
> >
> >
> > A is not an instance of A; see the ri info for Module#===.
> >
>
> At what point does it cause trouble for it to also return true if the
> class matches?

At the point where you call it :slight_smile: It can't do both; there would be no
way to interpret the result:

  case s
    when String ... # what do you do here? print it? instantiate it?

Hi --

> Hi --
>
>
> > Mauricio Fernández wrote:
> >
> > >
> > >>Why does this happen?
> > >>
> > >>class A
> > >>end
> > >>
> > >>A == A #1 => true
> > >>A === A #2 => false
> > >>
> > >>But 2 doesn't make any sense to me, why doesn't an A match another A?
> > >>Is this just something that slipped through or is there a specific
> > >>reason for this?
> > >
> > >
> > > A is not an instance of A; see the ri info for Module#===.
> > >
> >
> > At what point does it cause trouble for it to also return true if the
> > class matches?
>
> At the point where you call it :slight_smile: It can't do both; there would be no
> way to interpret the result:
>
> case s
> when String ... # what do you do here? print it? instantiate it?
>

Sorry perhaps I didn't word my question well. I meant when does it cause a
problem for a class x to match itself? What code fails if A === A results in
true? Why would you ever want to match the descendents and instances of A but
not match A itself.

It doesn't match the descendants of A, but rather instances of the
descendants of A:

  irb(main):001:0> class A; end; class B < A; end
  => nil
  irb(main):002:0> case A
  irb(main):003:1> when B: puts "yes" end
  => nil
  irb(main):004:0> case B
  irb(main):005:1> when A: puts "yes" end
  => nil
  irb(main):006:0> case B.new
  irb(main):007:1> when A: puts "yes" end
  yes

It's really the same test as #is_a? The docs should probably add
an "of":

  Returns true if anObject is an instance of mod or of
  one of mod's descendents.

David

···

On Thu, 8 Jul 2004, Charles Comstock wrote:

On Thu, 8 Jul 2004, David A. Black wrote:
> On Thu, 8 Jul 2004, Charles Comstock wrote:
> > > On Wed, Jul 07, 2004 at 04:22:36PM +0900, Charles Comstock wrote:

--
David A. Black
dblack@wobblini.net

Why is that this is defined like that and not like this:

  Case Equality --- Returns true if anObject is an instance of mod, one of mod's
descendents or _is mod_.

Because then it would match some instances of class Class, instances of
mod and instances of descendants of mod. That's three separate things,
two closely related and a third that's not.

It doesn't match descendants of mod -- it matches instances of
descendants of mod.

I can't think of any use-case where matching instances of Class in
addition to instances of Mod would be useful.

I understand what it matches, that is not the purpose of my line of questions, I
am asking why it doesn't also match itself, and for what reason you would want
to match descendents of x but not x itself. Does anyone have a logical reason
why this is the case?

I think you confuse the kind_of? relation with the equals or "is"
relation.

Class kind_of Class
Class kind_of Object
Object kind_of Object
Class is Class
mod is_a Class
instance kind_of mod
instance kind_of Object

but not

instance kind_of Class
instance is_a Class

Ari

He is asking why isn't === defined as

class Module
   def ===(obj)
      obj.is_a?(self) or obj == self
   end
end

or maybe

class Module
   def ===(obj)
      obj.is_a?(self) or obj.ancestors.include?(self)
   end
end

Not that I advocate either definition.

···

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

On Thu, 8 Jul 2004, Charles Comstock wrote:
> Sorry perhaps I didn't word my question well. I meant when does it cause a
> problem for a class x to match itself? What code fails if A === A results in
> true? Why would you ever want to match the descendents and instances of A but
> not match A itself.

It doesn't match the descendants of A, but rather instances of the
descendants of A:
[...]

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around

Er, scratch this definition. obj.is_a?(self) is the same thing
as obj.ancestors.include?(self) AFAIK.

···

--- Jeff Mitchell <quixoticsycophant@yahoo.com> wrote:

or maybe

class Module
   def ===(obj)
      obj.is_a?(self) or obj.ancestors.include?(self)
   end
end

__________________________________
Do you Yahoo!?
Yahoo! Mail - 50x more storage than other providers!
http://promotions.yahoo.com/new_mail

Hi --

> > Sorry perhaps I didn't word my question well. I meant when does it cause a
> > problem for a class x to match itself? What code fails if A === A results in
> > true? Why would you ever want to match the descendents and instances of A but
> > not match A itself.
>
> It doesn't match the descendants of A, but rather instances of the
> descendants of A:
> [...]

He is asking why isn't === defined as

Right, but the question becomes less relevant given that the method
doesn't actually match the descendants of A -- so the answer is: you
wouldn't, and it doesn't :slight_smile:

class Module
   def ===(obj)
      obj.is_a?(self) or obj == self
   end
end

or maybe

class Module
   def ===(obj)
      obj.is_a?(self) or obj.ancestors.include?(self)
   end
end

I think you mean obj.class.ancestors, in which case is_a? should take
care of it -- which means that your second definition is actually how
the method works, I believe.

As for the first, and Charles's question about why it doesn't work
that way: look again at my earlier example:

  case s
    when String ...

With the "is_a? or ==" implementation, this 'when' clause could mean
either that s is String (the Class object) or that s is "Hi, how are
you?" (a String object). Knowing that something is either String or a
string, but not knowing which, isn't very valuable; it's hard to imagine
a case where you would act on that kind of information.

David

···

On Thu, 8 Jul 2004, Jeff Mitchell wrote:

--- "David A. Black" <dblack@wobblini.net> wrote:
> On Thu, 8 Jul 2004, Charles Comstock wrote:

--
David A. Black
dblack@wobblini.net

David A. Black schrieb:

As for the first, and Charles's question about why it doesn't work
that way: look again at my earlier example:

  case s
    when String ...

With the "is_a? or ==" implementation, this 'when' clause could mean
either that s is String (the Class object) or that s is "Hi, how are
you?" (a String object). Knowing that something is either String or a
string, but not knowing which, isn't very valuable; it's hard to imagine
a case where you would act on that kind of information.

Ack.

And a question to the OP: why do you want to apply the case statement to classes instead of to their instances? What should this case statement do (depending on which class is given)? Maybe we can find an alternative to what you think you need.

Regards,
Pit

I think you mean obj.class.ancestors, in which case is_a? should take
care of it -- which means that your second definition is actually how
the method works, I believe.

As for the first, and Charles's question about why it doesn't work
that way: look again at my earlier example:

  case s
    when String ...

With the "is_a? or ==" implementation, this 'when' clause could mean
either that s is String (the Class object) or that s is "Hi, how are
you?" (a String object). Knowing that something is either String or a
string, but not knowing which, isn't very valuable; it's hard to imagine
a case where you would act on that kind of information.

I don't think you would generally pass either an instance of string or String to a case statement like this, so it shouldn't generally matter. However I think I found a better logical explanation as to why this is false.

String is a constant name that represents a String, however constants names that represent String are not strings themselves, so therefor it doesn't match. Thus if s holds String it is holding a reference to a constant name and not to a String, so it doesn't match.

Which makes sense, and I can accept that logic. Sorry I guess I wasn't making myself clear in what I was asking and so alot of your responses were to the question of what Module#=== is useful for, as opposed to answering my question which was "why doesn't A === A return true". Which has now been answered.

So now I revise my question:

Given that A === A logically returns false, is there a good way in a case statement to check if these things are the same by some other property? For instance a = A; a.id === A.id returns true, but is that a logical way to test this? Or is there another way?

   Charles Comstock

Ack.

And a question to the OP: why do you want to apply the case statement to classes instead of to their instances? What should this case statement do (depending on which class is given)? Maybe we can find an alternative to what you think you need.

Regards,
Pit

   def parse(str)
     input = str.chomp.split
     comm = CommandFactory[input[0]]

     case comm
     when StartCommand
       comm.new(input[1],input[2..-1])
       @mobile = input[1]
     when HelpCommand
     else
       comm.new(@mobile,input[1..-1])
     end
   end

CommandFactory returns the name of the class so that can instantiate it later with different arguments. Normally all commands reference who they are from but the start command and help are different since the start command defines who it is from, and the help command needs to be accessable before and after the start command has actually instantiated a character. I could pass these as parameters later on through a different function, but this seemed a logical way to do it. I think this should work:

   def parse(str)
     input = str.chomp.split
     comm = CommandFactory[input[0]]

     case comm.id
     when StartCommand.id
       comm.new(input[1],input[2..-1])
       @mobile = input[1]
     when HelpCommand.id
     else
       comm.new(@mobile,input[1..-1])
     end
   end

Since we are matching id's now which is valid. The command factory part is actually pretty slick too, there is a Command superclass and whenever anything derives from it, self.inherited gets called on Command, which then grabs the singleton instance of CommandFactory and adds the inherited class name to a hash in CommandFactory. So CommandFactory has a hash which amoung other things includes {"start" => StartCommand, "help" => HelpCommand}, so when I parse the commandline I just grab the needed command right out of the CommandFactory and then instantiate it with the rest of the commandline parameters.

It's just a stupid mud project I am doing to test out pattern based software design. When it's finished I'll release it to the community as example code.

I think id should fix my problem, it just suprised me when it didn't match the name like I thought it would.

Charles Comstock

Hi --

>
> I think you mean obj.class.ancestors, in which case is_a? should take
> care of it -- which means that your second definition is actually how
> the method works, I believe.
>
> As for the first, and Charles's question about why it doesn't work
> that way: look again at my earlier example:
>
> case s
> when String ...
>
> With the "is_a? or ==" implementation, this 'when' clause could mean
> either that s is String (the Class object) or that s is "Hi, how are
> you?" (a String object). Knowing that something is either String or a
> string, but not knowing which, isn't very valuable; it's hard to imagine
> a case where you would act on that kind of information.
>

I don't think you would generally pass either an instance of string or
String to a case statement like this, so it shouldn't generally matter.

The problem is more fundamental, though: you would be unable to know
why "when String" had returned true, so you'd have to test it further
-- so the case statement would have done you no good. If you're sure
in advance, then having the double behavior isn't necessary; and if
you're not sure, then the double behavior makes the method useless.

  However I think I found a better logical explanation as to why this is
false.

String is a constant name that represents a String, however constants
names that represent String are not strings themselves, so therefor it
doesn't match. Thus if s holds String it is holding a reference to a
constant name and not to a String, so it doesn't match.

Which makes sense, and I can accept that logic. Sorry I guess I wasn't
making myself clear in what I was asking and so alot of your responses
were to the question of what Module#=== is useful for, as opposed to
answering my question which was "why doesn't A === A return true".

You were clear; my point was that if A === A is true, and A === A.new
is also true, the === method isn't giving you any useful information,
which in turn is a reason for its not being designed that way. As for
the design choice between the two, my impression has always been that
Matz felt the #is_a? one would be more likely to be useful than the
#== one.

Which has now been answered.

So now I revise my question:

Given that A === A logically returns false, is there a good way in a
case statement to check if these things are the same by some other
property? For instance a = A; a.id === A.id returns true, but is
that a logical way to test this? Or is there another way?

id should achieve what you want, or maybe you could use an if
statement instead of a case statment.

David

···

On Thu, 8 Jul 2004, Charles Comstock wrote:

--
David A. Black
dblack@wobblini.net