Comparing Classes with Case?

The following failure surprised me:

Version 1:

irb(main):068:0> "hi".class == "hi".class
=> true
irb(main):069:0> "hi".class === "hi".class
=> false

Version 2:

irb(main):055:0> case "whatever".class
irb(main):056:1> when String
irb(main):057:1> p "it is a string"
irb(main):058:1> else
irb(main):059:1* p "it is not a string"
irb(main):060:1> end
"it is not a string"

What it amounts to is you can't use case/when to test for class. This is a HUGE LOSE.
The pickaxe book .chm says YOU CAN do it. I'm using ruby 1.8.2

Warren

Warren Seltzer wrote:

The following failure surprised me:

Version 1:

irb(main):068:0> "hi".class == "hi".class
=> true
irb(main):069:0> "hi".class === "hi".class
=> false

Version 2:

irb(main):055:0> case "whatever".class
irb(main):056:1> when String
irb(main):057:1> p "it is a string"
irb(main):058:1> else
irb(main):059:1* p "it is not a string"
irb(main):060:1> end
"it is not a string"

What it amounts to is you can't use case/when to test for class.
This is a HUGE LOSE. The pickaxe book .chm says YOU CAN do it. I'm
using ruby 1.8.2

You're using it wrong:

case "whatever"
when String; p "is string"
else p "no string"
end

"is string"
=> nil

Kind regards

    robert

Selon Warren Seltzer <warrens@actcom.net.il>:

The following failure surprised me:

Version 1:

irb(main):068:0> "hi".class == "hi".class
=> true
irb(main):069:0> "hi".class === "hi".class
=> false

Version 2:

irb(main):055:0> case "whatever".class
irb(main):056:1> when String
irb(main):057:1> p "it is a string"
irb(main):058:1> else
irb(main):059:1* p "it is not a string"
irb(main):060:1> end
"it is not a string"

What it amounts to is you can't use case/when to test for class. This is a
HUGE LOSE.
The pickaxe book .chm says YOU CAN do it. I'm using ruby 1.8.2

Lose the ".class". === automatically looks for the class of the left argument.
In your case example, just write:
case "whatever"
when String
  p "it is a string"
else
  p "it is not a string"
end

It's work like a charm. Basically "a === SomeClass" is the same as "a.class ==
SomeClass".

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

On the right track, but it's actually the right argument. That is:

  irb(main):001:0> String === "string"
  => true

That's because the left hand side is the receiver (=== is just another
method). When the receiver is a Class, Class#=== is called.

  irb(main):002:0> String.===( "string" )
  => true

This method takes the class of the argument and compares it to self using ==.

  irb(main):003:0> String == "string".class
  => true

However, if you get the order wrong, you end up calling (in this
example) String#===. String#=== ensures the argument (right hand side)
is_a? String and has the same value. Since the constant String is a
Class, not a String, the comparison fails.

  irb(main):004:0> "string" === String
  => false

A when clause for a case statement evaluates using === with the when
argument as the receiver and case argument as argument. So:

  case "string"
  when String: puts "is string"
  when Integer: puts "is integer"
  else puts "don't know"
  end

is exactly equivalent to:

  if String.===( "string" )
    puts "is string"
  elsif Integer.===( "string" )
    puts "is integer"
  else
    puts "don't know"
  end

Hope that helps clear things up.

Jacob Fugal

···

On 10/25/05, Christophe Grandsire <christophe.grandsire@free.fr> wrote:

Lose the ".class". === automatically looks for the class of the left argument
... Basically "a === SomeClass" is the same as "a.class == SomeClass".

Just throwing in that it would not be a "BIG LOSS", not even a _big
loss_ or a big loss, because you nearly never should test for the
class of an object.

bschroed@black:~/svn/projekte/ruby-things$ cat undef.rb
class UppercaseString
  def initialize(string)
    @string = string.upcase
  end

  def gsub(*args, &block)
    self.class.new(@string.gsub(*args, &block))
  end

  def to_s
    @string.dup
  end
end

a = "quack like a duck"
b = UppercaseString.new("walk like a duck")

puts a.gsub(/duck/i, 'cow')

puts b.gsub(/duck/i, 'cow')

class <<a
  undef_method "gsub"
end
puts a.gsub(/0*/, '42')
bschroed@black:~/svn/projekte/ruby-things$ ruby undef.rb
quack like a cow
WALK LIKE A COW
undef.rb:25: undefined method `gsub' for "quack like a duck":String
(NoMethodError)

now imagine require would have been implemented like this:

def require(filename)
  case filename
  when String
    load filename.gsub('\\', '/')
  when Symbol
    require(filename.to_s)
  when Fixnum
    raise "Can require only once"
  else
    raise "Don't know what to do"
  end
end

Then require would only work with real strings and symbols but not
with our stringlike new class. And with strings that behave special
(like our a) it would not work. So always use respond_to? instead of a
class test.

Maybe I should have used a standard ducktyping example, my own is a
bit week, but I hope it help.

cheers,

Brian

···

On 25/10/05, Christophe Grandsire <christophe.grandsire@free.fr> wrote:

Selon Warren Seltzer <warrens@actcom.net.il>:

> The following failure surprised me:
>
> Version 1:
>
> irb(main):068:0> "hi".class == "hi".class
> => true
> irb(main):069:0> "hi".class === "hi".class
> => false
>
>
> Version 2:
>
> irb(main):055:0> case "whatever".class
> irb(main):056:1> when String
> irb(main):057:1> p "it is a string"
> irb(main):058:1> else
> irb(main):059:1* p "it is not a string"
> irb(main):060:1> end
> "it is not a string"
>
> What it amounts to is you can't use case/when to test for class. This is a
> HUGE LOSE.
> The pickaxe book .chm says YOU CAN do it. I'm using ruby 1.8.2
>

Lose the ".class". === automatically looks for the class of the left argument.
In your case example, just write:
case "whatever"
when String
  p "it is a string"
else
  p "it is not a string"
end

It's work like a charm. Basically "a === SomeClass" is the same as "a.class ==
SomeClass".
--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

[snip example]

StringIO vs IO is the best example of this. Except for a few methods (like grabing the file descriptor) a StringIO behaves exactly like any IO. I use this when testing things that interact with IO objects. A StringIO feels much better than a Tempfile.

···

On Oct 25, 2005, at 9:09 AM, Brian Schröder wrote:

On 25/10/05, Christophe Grandsire <christophe.grandsire@free.fr> > wrote:
Just throwing in that it would not be a "BIG LOSS", not even a _big
loss_ or a big loss, because you nearly never should test for the
class of an object.

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Just throwing in that it would not be a "BIG LOSS", not even a _big
loss_ or a big loss, because you nearly never should test for the
class of an object.

Well, is the following the rare case of 'nearly never' or just
stupidity?

···

--------------------------------------------------
#!/opt/ruby/1.8/bin/ruby -w

class AnonUser ; end
class RegularUser < AnonUser ; end
class Developer < RegularUser ; end
class Guru < Developer ; end
class Sysop < Guru ; end

brian=Guru.new

# ...

if Developer===brian
  puts "ok, you are Developer"
end
if Sysop===brian
  puts "you may shutdown the server"
end
--------------------------------------------------

# -> ok, you are Developer

Patrick

En réponse à Jacob Fugal :

On the right track, but it's actually the right argument. That is:

  irb(main):001:0> String === "string"
  => true

You're right of course! I keep forgetting that order :frowning: .

That's because the left hand side is the receiver (=== is just another
method). When the receiver is a Class, Class#=== is called.

  irb(main):002:0> String.===( "string" )
  => true

It makes sense, but I still have difficulties remembering it :frowning: .

A when clause for a case statement evaluates using === with the when
argument as the receiver and case argument as argument. So:

Yes, and although the order makes sense, from a functional point of view, it is difficult to get my head around the idea that case indicates the argument rather than the receiver.

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

You need a straight mind to invent a twisted conlang.

One thing that may be bad about this approach is that you can't have
multiple inheritance. You should at least use modules extending a User
class, so that you allow for a more interesting hirarchie. And I don't
see why User should not have a set of properties and methods like

    * developer?
    * sysop?

etc. (Created dynamically, naturally :slight_smile:

That are my thoughts about the approach.

if user.developer?

···

On 25/10/05, Patrick Gundlach <clr8.10.randomuser@spamgourmet.com> wrote:

> Just throwing in that it would not be a "BIG LOSS", not even a _big
> loss_ or a big loss, because you nearly never should test for the
> class of an object.

Well, is the following the rare case of 'nearly never' or just
stupidity?

--------------------------------------------------
#!/opt/ruby/1.8/bin/ruby -w

class AnonUser ; end
class RegularUser < AnonUser ; end
class Developer < RegularUser ; end
class Guru < Developer ; end
class Sysop < Guru ; end

brian=Guru.new

# ...

if Developer===brian
  puts "ok, you are Developer"
end
if Sysop===brian
  puts "you may shutdown the server"
end
--------------------------------------------------

# -> ok, you are Developer

Patrick

  ###
elsif user.sysop?
  ###
end

reads better in my opinion.

regards,

Brian

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

Christophe Grandsire wrote:

En réponse à Jacob Fugal :

On the right track, but it's actually the right argument. That is:

  irb(main):001:0> String === "string"
  => true

You're right of course! I keep forgetting that order :frowning: .

That's because the left hand side is the receiver (=== is just
another method). When the receiver is a Class, Class#=== is called.

  irb(main):002:0> String.===( "string" )
  => true

It makes sense, but I still have difficulties remembering it :frowning: .

A when clause for a case statement evaluates using === with the when
argument as the receiver and case argument as argument. So:

Yes, and although the order makes sense, from a functional point of
view, it is difficult to get my head around the idea that case
indicates the argument rather than the receiver.

If you think about it for a moment you'll see that it's the only
reasonable approach: the expression after the case is the item to test and
all expressions after when denomiate conditions. You can for example do
this, which might make it clearer...

def Condition(&b)
  class <<b
    alias ===
  end
  b
end

case a_number
  when Condition {|x| x < 10}
  ...
end

Of course, you can have this easier as in

case
  when a_number < 10
  ...
end

But for more complicated conditions you might want to assign it to a const
so you can reuse it efficiently.

Kind regards

    robert

class AnonUser ; end
class RegularUser < AnonUser ; end
class Developer < RegularUser ; end
class Guru < Developer ; end
class Sysop < Guru ; end

[...]

One thing that may be bad about this approach is that you can't have
multiple inheritance. You should at least use modules extending a User
class, so that you allow for a more interesting hirarchie. And I don't
see why User should not have a set of properties and methods like

    * developer?
    * sysop?

etc. (Created dynamically, naturally :slight_smile:

That are my thoughts about the approach.

Thanks for your thoughts. The above (my approach) fits so nicely with
the single table inheritance in Rails without implementing a single
line of code (except for the five lines above). With this, I get never
above the multiple of 500 (or was it 5000?) lines of code to get a
higher patrick.to_i value. But the #developer? (and alike) methods read
better, right.

Patrick

Selon Robert Klemme <bob.news@gmx.net>:

>
> Yes, and although the order makes sense, from a functional point of
> view, it is difficult to get my head around the idea that case
> indicates the argument rather than the receiver.

If you think about it for a moment you'll see that it's the only
reasonable approach: the expression after the case is the item to test and
all expressions after when denomiate conditions.

Of course it is the only reasonable approach. I agree completely with it. But it
doesn't change that the order in which the case and the when parts read kind of
imply a left-to-right, rather than right-to-left, relationship, especially
since in other languages I know with a case/switch expression it's exactly what
happen under the hood :slight_smile: .

So although I see why it *should* be the way it is in Ruby, I still can't
remember it reliably. It's not a Ruby problem, it's a me problem :wink: .

  You can for example do

this, which might make it clearer...

def Condition(&b)
  class <<b
    alias ===
  end
  b
end

Wow! Some of the things one can do with Ruby make my head spin! The worst is: I
can understand what it means!!! :wink:

case a_number
  when Condition {|x| x < 10}
  ...
end

Cool trick indeed :slight_smile: . But I'll just try to remember the order from now on, or
at least remember how to get back to it...

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Thanks for your thoughts. The above (my approach) fits so nicely with
the single table inheritance in Rails without implementing a single
line of code (except for the five lines above). With this, I get never
above the multiple of 500 (or was it 5000?) lines of code to get a
higher patrick.to_i value. But the #developer? (and alike) methods read
better, right.

Patrick

Of course, you asked a Ruby question, so you got a Ruby answer.
If you had asked a Rails question, you may have gotten a Rails answer.

You should be more specific about that fact, it would get better responses.

Christophe Grandsire wrote:

Selon Robert Klemme <bob.news@gmx.net>:

Yes, and although the order makes sense, from a functional point of
view, it is difficult to get my head around the idea that case
indicates the argument rather than the receiver.

If you think about it for a moment you'll see that it's the only
reasonable approach: the expression after the case is the item to
test and all expressions after when denomiate conditions.

Of course it is the only reasonable approach. I agree completely with
it. But it doesn't change that the order in which the case and the
when parts read kind of imply a left-to-right, rather than
right-to-left, relationship, especially since in other languages I
know with a case/switch expression it's exactly what happen under the
hood :slight_smile: .

Just out of curiosity: which languages are you referring to and what do
they do?

So although I see why it *should* be the way it is in Ruby, I still
can't remember it reliably. It's not a Ruby problem, it's a me
problem :wink: .

  You can for example do

this, which might make it clearer...

def Condition(&b)
  class <<b
    alias ===
  end
  b
end

Wow! Some of the things one can do with Ruby make my head spin! The
worst is: I can understand what it means!!! :wink:

Pity you... :-))

case a_number
  when Condition {|x| x < 10}
  ...
end

Cool trick indeed :slight_smile: . But I'll just try to remember the order from
now on, or at least remember how to get back to it...

Ok, print this out in a bold 64pt font "I must never forget that in a CASE
statement conditions have their === invoked." :slight_smile:

Kind regards

    robert

Thanks for your thoughts. The above (my approach) fits so nicely with
the single table inheritance in Rails without implementing a single
line of code (except for the five lines above). With this, I get never
above the multiple of 500 (or was it 5000?) lines of code to get a
higher patrick.to_i value. But the #developer? (and alike) methods read
better, right.

Of course, you asked a Ruby question, so you got a Ruby answer.
If you had asked a Rails question, you may have gotten a Rails answer.

You should be more specific about that fact, it would get better responses.

Actually I am happy that I did not ask a rails specific question,
since I might have missed Brian's valuable comments. BTW: I have used
the code I posted in another project of mine, which is not rails
based. And, I see it more of a discussion where I get help
rethinking/recoding/refactoring. My answer to Brian was not supposed
to sound like 'well, thanks for your post, but I use my code anyway'.
If it did, I apologize.

Patrick

Selon Robert Klemme <bob.news@gmx.net>:

Just out of curiosity: which languages are you referring to and what do
they do?

The only languages I've ever used that I know have a switch/case statement are
Pascal, C and LINC (a database-related language from Unisys). AFAIK, in all
those cases the case statements are equivalent to using an if/elsif/else
construction, with the expression introduced by the switch/case being at the
*left* of the equal sign, and the different possibilities at the right. It is
especially true of LINC, where the case statement can only introduce a
variable, while the different choices can be literals (and literals in LINC
can't be at the left of a condition expression).

>
> Wow! Some of the things one can do with Ruby make my head spin! The
> worst is: I can understand what it means!!! :wink:

Pity you... :-))

When I see this kind of programs, I always think: there's just no way I should
be able to understand this! I am whether completely wrong in my understanding,
or getting insane! (you have to realise I've never had true IT or CS education
in my entire life)

Ok, print this out in a bold 64pt font "I must never forget that in a CASE
statement conditions have their === invoked." :slight_smile:

It will get lost in the moving (I'm moving within a month). But I'll keep this
post, for security :slight_smile: .

Thanks!

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Hello Patrick,

I can assure that your post sounded completely polite and sensible to
me. In fact I wonder that no one on this list has already made an
argument against my observations, as there are always new directions
to look at every problem. Thats why we all read this list. Its all a
complex graph of thought stimulations.

regards,

Brian

···

On 26/10/05, Patrick Gundlach <clr8.10.randomuser@spamgourmet.com> wrote:

>> Thanks for your thoughts. The above (my approach) fits so nicely with
>> the single table inheritance in Rails without implementing a single
>> line of code (except for the five lines above). With this, I get never
>> above the multiple of 500 (or was it 5000?) lines of code to get a
>> higher patrick.to_i value. But the #developer? (and alike) methods read
>> better, right.

> Of course, you asked a Ruby question, so you got a Ruby answer.
> If you had asked a Rails question, you may have gotten a Rails answer.
>
> You should be more specific about that fact, it would get better responses.

Actually I am happy that I did not ask a rails specific question,
since I might have missed Brian's valuable comments. BTW: I have used
the code I posted in another project of mine, which is not rails
based. And, I see it more of a discussion where I get help
rethinking/recoding/refactoring. My answer to Brian was not supposed
to sound like 'well, thanks for your post, but I use my code anyway'.
If it did, I apologize.

Patrick

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

Actually I am happy that I did not ask a rails specific question,
since I might have missed Brian's valuable comments. BTW: I have used
the code I posted in another project of mine, which is not rails
based. And, I see it more of a discussion where I get help
rethinking/recoding/refactoring. My answer to Brian was not supposed
to sound like 'well, thanks for your post, but I use my code anyway'.
If it did, I apologize.

Patrick

My response certainly wasn't meant to scold, only to point out that
answers will vary based on the context, and so the context should
always be presented as well. If you were actually looking for a
general discussion... well, I guess I wouldn't have thought that you
would've brought up Rails at all.

Christophe Grandsire wrote:

Selon Robert Klemme <bob.news@gmx.net>:

Just out of curiosity: which languages are you referring to and what
do they do?

The only languages I've ever used that I know have a switch/case
statement are Pascal, C and LINC (a database-related language from
Unisys). AFAIK, in all those cases the case statements are equivalent
to using an if/elsif/else construction, with the expression
introduced by the switch/case being at the *left* of the equal sign,
and the different possibilities at the right. It is especially true
of LINC, where the case statement can only introduce a variable,
while the different choices can be literals (and literals in LINC
can't be at the left of a condition expression).

Interesting to learn. Note however, that - if my rusty PASCAL doesn't
fool me - for PASCAL and C only scalar types are allowed for cases and
equality is commutative for these. So the convention having the var at
the left side in the if/else cascade may only because it reads more
natural for humans but it may actually have no meaning with respect to
language implementation (and it doesn't even change the semantics in these
cases). Note also that the if/else can become quite complex for C because
of fallthrough.

I don't know LINC but from what you say it sounds as if it's actually
different there.

Wow! Some of the things one can do with Ruby make my head spin! The
worst is: I can understand what it means!!! :wink:

Pity you... :-))

When I see this kind of programs, I always think: there's just no way
I should be able to understand this! I am whether completely wrong in
my understanding, or getting insane! (you have to realise I've never
had true IT or CS education in my entire life)

Then you probably get it from Ruby... :slight_smile: Seriously, it doesn't hurt to
read through some good books on algorithms and data structures to
understand why a Hash is more efficient than an Array in certain scenarios
etc.

Ok, print this out in a bold 64pt font "I must never forget that in
a CASE statement conditions have their === invoked." :slight_smile:

It will get lost in the moving (I'm moving within a month). But I'll
keep this post, for security :slight_smile: .

I was just going to offer to repost it... :-)))

Kind regards

    robert