Tricky problem with ===

I have a "BlankState" type of class and it's method_missing forwards
everything to an instance variable. This way I can treat the object
specially in my own code and all other code doesn't need to know the
difference. For a contrived example:

class Proxy
  # remove methods inherited by Object, etc
  instance_methods.each do |m|
    undef_method m unless m =~ /^__/
  end

  def initialize name, value
    @name, @value = name, value
  end

  def to_myformat
    "#{@name}: #{@value}"
  end

  def method_missing name, *args, &block
    @value.send name, *args, &block
  end
end

object = "regular string"
x = Proxy.new("UID", object)

x.class # => Array
x.size # => 5
x.to_myformat # => UID: regular string

While that's a pretty boring example, the idea might come in handy for
something like a graph node, or linked list node (etc). Anyway, the
problem I have is the way this works:

case x
when Array puts "Array"
when Proxy puts "Proxy"
end

This calls Array.===(x) which doesn't call x.class, and it returns
false. If I redefine Array.===(other) to test self == other.class then
it works. I can't think of any way to get this to work since it
doesn't appear there are any methods of x being called that I can
intercept. Does ruby get the class 'internally' instead of calling
x.class? I guess if worse comes to worse I can use if x.is_a? Array;
...; elsif x.is_a? Foo; ...; end. I don't want to go about redefining
every class's === method, that seems to be a mess.

Erwin

I have a "BlankState" type of class and it's method_missing forwards
everything to an instance variable. This way I can treat the object
specially in my own code and all other code doesn't need to know the
difference. For a contrived example:

class Proxy
# remove methods inherited by Object, etc
instance_methods.each do |m|
   undef_method m unless m =~ /^__/
end

def initialize name, value
   @name, @value = name, value
end

def to_myformat
   "#{@name}: #{@value}"
end

def method_missing name, *args, &block
   @value.send name, *args, &block
end
end

object = "regular string"
x = Proxy.new("UID", object)

x.class # => Array
x.size # => 5
x.to_myformat # => UID: regular string

What I see when I evaluate your code is:

    x.class # => String
    x.size # => 14

While that's a pretty boring example, the idea might come in handy for
something like a graph node, or linked list node (etc). Anyway, the
problem I have is the way this works:

case x
when Array puts "Array"
when Proxy puts "Proxy"
end

If you really have to do this kind of thing, a work-around you might try is:

    result = case x.class.new
             when Array then "Array"
             when String then "String"
             when Proxy then "Proxy"
             end
    result # => "String"

That's kind of ugly, but then I think using case statements to distinguish the class of an object is kind of ugly under any circumstances.

Regards, Morton

···

On May 26, 2007, at 11:13 PM, Erwin Abbott wrote:

Erwin Abbott wrote:

case x
when Array puts "Array"
when Proxy puts "Proxy"
end

Ugly, but you could try:
puts case [x.class]
  when [Array]: "Array"
  when [Proxy]: "Proxy"
  else "Hu?"
end

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

I have a "BlankState" type of class and it's method_missing forwards
everything to an instance variable. This way I can treat the object
specially in my own code and all other code doesn't need to know the
difference. For a contrived example:

class Proxy
# remove methods inherited by Object, etc
instance_methods.each do |m|
   undef_method m unless m =~ /^__/
end

def initialize name, value
   @name, @value = name, value
end

def to_myformat
   "#{@name}: #{@value}"
end

def method_missing name, *args, &block
   @value.send name, *args, &block
end
end

object = "regular string"
x = Proxy.new("UID", object)

Erwin, are you aware of delegate?

x.class # => Array
x.size # => 5
x.to_myformat # => UID: regular string

What I see when I evaluate your code is:

   x.class # => String
   x.size # => 14

While that's a pretty boring example, the idea might come in handy for
something like a graph node, or linked list node (etc). Anyway, the
problem I have is the way this works:

case x
when Array puts "Array"
when Proxy puts "Proxy"
end

If you really have to do this kind of thing, a work-around you might try is:

   result = case x.class.new
            when Array then "Array"
            when String then "String"
            when Proxy then "Proxy"
            end
   result # => "String"

That's kind of ugly, but then I think using case statements to distinguish the class of an object is kind of ugly under any circumstances.

It's not only ugly it probably also does not make much sense because the OP wants to test "x" and not "x.class.new".

A better solution would be to use the second form of "case" to have more control over the test

case
   when Array == x.class
   when String == x.class
end

An alternative way is to build custom testers:

ARRAY = lambda {|x| Array === x.class}
def ARRAY.===(o) self[o] end

case x
   when ARRAY
...
end

Viewing this more abstract: maybe case isn't even the right thing to do. We would have to know more about what the OP is trying to accomplish.

On an even more general level there is a certain contradiction between having something behave exactly like an Array - but also different... :slight_smile:

Kind regards

  robert

···

On 27.05.2007 07:04, Morton Goldberg wrote:

On May 26, 2007, at 11:13 PM, Erwin Abbott wrote:

case uses === to match when clauses. Therefor you can do it like this:

case x
when Array
   puts "Array"
when Proxy
   puts "Proxy
end

Cheers-

-- Ezra Zygmuntowicz -- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)

···

On May 27, 2007, at 7:24 AM, Stefan Rusterholz wrote:

Erwin Abbott wrote:

case x
when Array puts "Array"
when Proxy puts "Proxy"
end

Ugly, but you could try:
puts case [x.class]
  when [Array]: "Array"
  when [Proxy]: "Proxy"
  else "Hu?"
end

Regards
Stefan

Erwin, are you aware of delegate?

Yes, but it's not transparent as I'd like. I don't want the delegating
class to answer methods like class and is_a? for example. It also
doesn't work when trying to distinguish the class with a case
construct.

What I see when I evaluate your code is:

   x.class # => String
   x.size # => 14

My mistake, I had an array to begin with and didn't remember to change
the results to match when I changed it to a string.

A better solution would be to use the second form of "case" to have more
control over the test

case
   when Array == x.class
   when String == x.class
end

That's not bad. I like it... and it's a little more explicit, too. I
didn't know you could use case that way, that seems pretty useful.

Viewing this more abstract: maybe case isn't even the right thing to do.
  We would have to know more about what the OP is trying to accomplish.

Well the idea is to wrap an object with this proxy class and not mess
up any code that doesn't know about it... it should seem completely
transparent. It gives you a separate namespace for methods and
instance variables, so you don't have to worry about redefining those
with a mixin or singleton method, but you can still specialize some
object by wrapping it.

I think using case statements to distinguish the class of an object is
kind of ugly under any circumstances.

I think it works pretty well, but I'd like to know what you prefer
instead. I realize distinguishing objects by class is not encouraged,
but I find myself needing it often, for better or worse.

On an even more general level there is a certain contradiction between
having something behave exactly like an Array - but also different... :slight_smile:

Yes, that's a good observation. Though I'm very happy with how close
I've gotten to making it happen.

···

On 5/27/07, Robert Klemme wrote:
On 27.05.2007 07:04, Morton Goldberg wrote:

Ezra Zygmuntowicz wrote:

  case uses === to match when clauses. Therefor you can do it like this:

Which doesn't work for him because his class isn't really an Array, just
pretends to be. He outlined that in his initial post.
If I misunderstood his initial post, feel free to correct me.

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

Yeah I think i jumped in without reading what he really wanted. My apologies.

Thanks
-- Ezra

···

On May 27, 2007, at 3:49 PM, Stefan Rusterholz wrote:

Ezra Zygmuntowicz wrote:

  case uses === to match when clauses. Therefor you can do it like this:

Which doesn't work for him because his class isn't really an Array, just
pretends to be. He outlined that in his initial post.
If I misunderstood his initial post, feel free to correct me.

Regards
Stefan

--
Posted via http://www.ruby-forum.com/\.

Hi --

···

On Sun, 27 May 2007, Erwin Abbott wrote:

On 5/27/07, Robert Klemme wrote:

Erwin, are you aware of delegate?

Yes, but it's not transparent as I'd like. I don't want the delegating
class to answer methods like class and is_a? for example. It also
doesn't work when trying to distinguish the class with a case
construct.

On 27.05.2007 07:04, Morton Goldberg wrote:

What I see when I evaluate your code is:

   x.class # => String
   x.size # => 14

My mistake, I had an array to begin with and didn't remember to change
the results to match when I changed it to a string.

A better solution would be to use the second form of "case" to have more
control over the test

case
   when Array == x.class
   when String == x.class
end

That's not bad. I like it... and it's a little more explicit, too. I
didn't know you could use case that way, that seems pretty useful.

Viewing this more abstract: maybe case isn't even the right thing to do.
  We would have to know more about what the OP is trying to accomplish.

Well the idea is to wrap an object with this proxy class and not mess
up any code that doesn't know about it... it should seem completely
transparent. It gives you a separate namespace for methods and
instance variables, so you don't have to worry about redefining those
with a mixin or singleton method, but you can still specialize some
object by wrapping it.

I think using case statements to distinguish the class of an object is
kind of ugly under any circumstances.

I think it works pretty well, but I'd like to know what you prefer
instead. I realize distinguishing objects by class is not encouraged,
but I find myself needing it often, for better or worse.

But you're running aground on exactly why it isn't encouraged: namely,
it doesn't give you definitive information about the object's behavior
or interface. Are you sure a mixin wouldn't make more sense?

David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

Viewing this more abstract: maybe case isn't even the right thing to do.
  We would have to know more about what the OP is trying to accomplish.

Well the idea is to wrap an object with this proxy class and not mess
up any code that doesn't know about it... it should seem completely
transparent. It gives you a separate namespace for methods and
instance variables, so you don't have to worry about redefining those
with a mixin or singleton method, but you can still specialize some
object by wrapping it.

Yes, you said so earlier. But what business problem are you trying to solve?

I think using case statements to distinguish the class of an object is
kind of ugly under any circumstances.

I think it works pretty well, but I'd like to know what you prefer
instead. I realize distinguishing objects by class is not encouraged,
but I find myself needing it often, for better or worse.

On an even more general level there is a certain contradiction between
having something behave exactly like an Array - but also different... :slight_smile:

Yes, that's a good observation. Though I'm very happy with how close
I've gotten to making it happen.

The problem is that it seems to be a fine line between coming close and breaking. :slight_smile: I'd also consider mixins or other mechanisms. It all depends on what problem you are trying to solve...

Kind regards

  robert

···

On 27.05.2007 15:23, Erwin Abbott wrote:

On 5/27/07, Robert Klemme wrote:

I think it works pretty well, but I'd like to know what you prefer
instead. I realize distinguishing objects by class is not encouraged,
but I find myself needing it often, for better or worse.

But you're running aground on exactly why it isn't encouraged: namely,
it doesn't give you definitive information about the object's behavior
or interface.

Say you have some method that accepts various objects that represent a
time. It's job is to return true if the given time is more than a week
old. We have DateTime, Time, Date, and probably some more I don't know
about. We can test that these objects have a comparison operator, and
not pay attention to their class, but that doesn't help me because
Date.new < Time.now doesn't work. In fact, Date's <=> checks if it was
passed an Integer or a Date object, because knowing the object's class
is necesarry to know how to compare them.

I don't see any other way to write that method, am I missing
something? It seems the most definitive information about any objects
behavior is its source code, next to that we can ask what class it is
and look up the documentation. I agree distinguishing objects by class
has its problems, like Date#<=> doesn't work with anything but Dates
and Integers (when maybe it should be testing respond_to? :ajd when it
isn't given an Integer), but is there a better alternative?

Are you sure a mixin wouldn't make more sense?

Instead of using a proxy? It's a trade-off at this point between
getting case to work and having to write code that can't use intstance
variable names like @id or @name because the original object might
already be using them, or having to alias an existing #name as
#old_name and making sure my #name doesn't break any code in that
class. I might still break something with a proxy class answering
#name, but it won't break the underlying object when it calls name on
itself. I think at this point I'll accept that case statements won't
work, but I'm going to try a few other things before giving up.

Regards,
  Erwin

···

On 5/27/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

I'm not trying to solve a business problem (I'm not sure what you
mean?). I would think this is something more like a design pattern,
like http://home.earthlink.net/~huston2/dp/proxy.html

Here's some code that implements it

class Object
  def self.=== other
    #puts "object #{self} === #{other}"
    other.is_a? self
  end
end

class Thunk
  instance_methods.each do |m|
    undef_method m unless m =~ /^__/
  end

  def initialize &block
    @proc = block
    @computed = false
  end

  def method_missing method, *args, &block
    unless @computed
      puts "computing value"
      @computed = true
      @value = @proc.call
    end
    @value.send method, *args, &block
  end
end

$stdout.sync = true

# the block isn't called until any message is sent to x, this way
# if x is never used (maybe there's a condition that prevents this)
# we'll never have to bother performing a slow computation
x = Thunk.new { sleep 5; 'expensive computation...'; 50000 }; nil

if Time.now.sec.modulo(2).zero? # even
  puts "value: #{x}"
end

Regards,
  Erwin

PS: Yes, I am aware there have been other ruby projects that do
similar things. This is just an example, where any code that's passed
a "Thunk" doesn't need to know it's anything special... unless it's
using a case statement to check if it implements a mixin :wink:

···

On 5/28/07, Robert Klemme <shortcutter@googlemail.com> wrote:

> Well the idea is to wrap an object with this proxy class and not mess
> up any code that doesn't know about it... it should seem completely
> transparent. It gives you a separate namespace for methods and
> instance variables, so you don't have to worry about redefining those
> with a mixin or singleton method, but you can still specialize some
> object by wrapping it.

Yes, you said so earlier. But what business problem are you trying to
solve?

Hi --

I think it works pretty well, but I'd like to know what you prefer
instead. I realize distinguishing objects by class is not encouraged,
but I find myself needing it often, for better or worse.

But you're running aground on exactly why it isn't encouraged: namely,
it doesn't give you definitive information about the object's behavior
or interface.

Say you have some method that accepts various objects that represent a
time. It's job is to return true if the given time is more than a week
old. We have DateTime, Time, Date, and probably some more I don't know
about. We can test that these objects have a comparison operator, and
not pay attention to their class, but that doesn't help me because
Date.new < Time.now doesn't work. In fact, Date's <=> checks if it was
passed an Integer or a Date object, because knowing the object's class
is necesarry to know how to compare them.

I don't see any other way to write that method, am I missing
something? It seems the most definitive information about any objects
behavior is its source code, next to that we can ask what class it is
and look up the documentation. I agree distinguishing objects by class
has its problems, like Date#<=> doesn't work with anything but Dates
and Integers (when maybe it should be testing respond_to? :ajd when it
isn't given an Integer), but is there a better alternative?

Are you sure a mixin wouldn't make more sense?

Instead of using a proxy? It's a trade-off at this point between
getting case to work and having to write code that can't use intstance
variable names like @id or @name because the original object might
already be using them, or having to alias an existing #name as
#old_name and making sure my #name doesn't break any code in that
class. I might still break something with a proxy class answering
#name, but it won't break the underlying object when it calls name on
itself. I think at this point I'll accept that case statements won't
work, but I'm going to try a few other things before giving up.

I was thinking of something like (semi-tested only):

   module DateComp
     def <(other)
       Time.parse(strftime("%m/%d/%y")) < other
     end
   end

   d = Date.new.extend(DateComp)
   your_method(d)

That way, you can use duck typing in the method itself, which means
that you don't have to change the method if you want to provide it
with other objects; you just have to prepare things on the calling
end. You might have to have a more complex mixin for your proxy case,
but anyway, that's the kind of thing I was thinking about.

David

···

On Mon, 28 May 2007, Erwin Abbott wrote:

On 5/27/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

unknown wrote:

Hi --

time. It's job is to return true if the given time is more than a week
and look up the documentation. I agree distinguishing objects by class
#old_name and making sure my #name doesn't break any code in that
class. I might still break something with a proxy class answering
#name, but it won't break the underlying object when it calls name on
itself. I think at this point I'll accept that case statements won't
work, but I'm going to try a few other things before giving up.

I was thinking of something like (semi-tested only):

   module DateComp
     def <(other)
       Time.parse(strftime("%m/%d/%y")) < other
     end
   end

   d = Date.new.extend(DateComp)
   your_method(d)

That way, you can use duck typing in the method itself, which means
that you don't have to change the method if you want to provide it
with other objects; you just have to prepare things on the calling
end. You might have to have a more complex mixin for your proxy case,
but anyway, that's the kind of thing I was thinking about.

David

Universal Date/Time comparison is indeed difficult as neither your nor
mine (below) solution attribute for different TimeZones and/or DST.
I'd implement a universal comparison like below:

def <=>(other)
  [year, day_of_year, hour, minute, second] <=> [other.year,
other.day_of_year, other.hour, other.minute, other.second]
end

Regards
Stefan

···

On Mon, 28 May 2007, Erwin Abbott wrote:

--
Posted via http://www.ruby-forum.com/\.

I think I may have come to a solution with

class Object
  def self.=== other
    #puts "object: #{self} == #{other}"
    other.is_a? self
  end
end

It may not work in some special cases, but it seems to do the trick.
It appears this is only called when comparing to an instance of Class,
so case x when 55; true; end doesn't involve this method. I haven't
tested this much, so I may be horribly breaking something somewhere,
but so far it seems okay.

However, I haven't gotten it to work when comparing to a Module, so
x.extend(SomeMixin); case x when SomeMixin ... doesn't work.
Module.=== doesn't seem to be involved in this test.

Regards,
  Erwin

I was just trying to illustrate the need for using case to determine
an object's class. If we were to use mixins as an alternative, it's
still very complicated because we'll need to redefine Time#<=>,
Date#<=> and so on. Won't we now have to check if
other.is_a?(DateComp) and still handle the cases when
other.is_a?(self.class)... ? I don't see any difference, because now
we're relying on knowing about DateComp instead of Time or Date.

I agree mixins are useful in many cases, but maybe not in this one.
Another way to make code less rigid might be to still use the case
construct but have something that trusts the foreign object to do the
right thing in the default case. For example

class Date
  def <=> other
    case other
    when self.class # this case is redundant, with the default case below
      self.ajd <=> other.ajd
    when Numeric
      self.ajd <=> other
    else
      if other.respond_to? :ajd
        # hope that other.ajd means the same thing
        self.ajd <=> other.ajd
      else
        nil # causes ArgumentError (from Comparable probably)
      end
    end
  end
end

Then any class that's supposed to represent a date or time can
implement #ajd and be compatable. But we still had to check if other
was an Integer, because defining Numeric#ajd doesn't really make
sense.

Regards,

Erwin

···

On 5/28/07, Stefan Rusterholz <apeiros@gmx.net> wrote:

Universal Date/Time comparison is indeed difficult as neither your nor
mine (below) solution attribute for different TimeZones and/or DST.
I'd implement a universal comparison like below:

def <=>(other)
  [year, day_of_year, hour, minute, second] <=> [other.year,
other.day_of_year, other.hour, other.minute, other.second]
end

Hi --

Universal Date/Time comparison is indeed difficult as neither your nor
mine (below) solution attribute for different TimeZones and/or DST.
I'd implement a universal comparison like below:

def <=>(other)
  [year, day_of_year, hour, minute, second] <=> [other.year,
other.day_of_year, other.hour, other.minute, other.second]
end

I was just trying to illustrate the need for using case to determine
an object's class. If we were to use mixins as an alternative, it's
still very complicated because we'll need to redefine Time#<=>,
Date#<=> and so on. Won't we now have to check if
other.is_a?(DateComp) and still handle the cases when
other.is_a?(self.class)... ? I don't see any difference, because now
we're relying on knowing about DateComp instead of Time or Date.

You don't have to test for DateComp ancestry, though. You just assume
it (or equivalent) in your method, and then make sure that only
objects with that mixin (or equivalent) are passed to the method.
That way, you encapsulate just the operation itself in the method
(basically, duck typing), and the caller takes responsibility for only
sending along objects that fit the bill. (Ha ha, bill, duck, get it?
Oh, never mind :slight_smile:

David

···

On Mon, 28 May 2007, Erwin Abbott wrote:

On 5/28/07, Stefan Rusterholz <apeiros@gmx.net> wrote:

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)