Forum: Ruby can you enable coercion between types?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
larry (Guest)
on 2006-03-18 21:50
(Received via mailing list)
I have a ruby class called Duration that represents a unit of time.
It's main attribute is a number that represents the duration in
seconds.

If I try to save it to a DB using Rails, I get a "Duration can not be
coerced into Float" error.  Is there a way to modify the definition of
Duration to enable automatic coercion into a float?  (I'm thinking of
something like adding a  "to_float" method to the class.)

thanks in advance
larry
Trans (Guest)
on 2006-03-18 22:24
(Received via mailing list)
Did you try #to_f?

T.
unknown (Guest)
on 2006-03-19 02:35
(Received via mailing list)
On Sun, 19 Mar 2006, larry wrote:

> larry
try this:

     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
     end

     p(40.0 + Duration::new(2.0))
     p(40 + Duration::new(2))


     harp:~ > ruby a.rb
     42.0
     42

hth.

-a
Trans (Guest)
on 2006-03-19 03:20
(Received via mailing list)
removed_email_address@domain.invalid wrote:
>          else
>            [Float(@seconds), Float(other)]
>          end
>        end
>      end

Could you explain what this is doing? I don't get the effect.

Thanks,
T.
unknown (Guest)
on 2006-03-19 04:27
(Received via mailing list)
On Sun, 19 Mar 2006, Trans wrote:

>>          if other.class == @seconds.class
>>            [@seconds, other]
>>          else
>>            [Float(@seconds), Float(other)]
>>          end
>>        end
>>      end
>
> Could you explain what this is doing? I don't get the effect.

my understanding of coerce is that it's used to produce a pair of
operands -
myself and other - which can be used to perform operations.

so, in the case of Fixnum#+ we might see

   def + other
     x, y = other.coerce self
     x + y
   end

which is to say Fixnum need not know about how to add every possible
class -
rather it leaves that knowledge up to those classes to export via thier
coerce
method.  as i understand it that method should return two new objects
which
interoperate so, in the case above (which is made up) we might have

   duration = Duration::new 42.0

   42 + duration

here would call duration(42) and expect back an x and y which could be
added
together.  so, if @seconds happens to be a Fixnum the result will be
another
Fixnum.  if @seconds happens to a Float the result would be a Float.

i admit that i'm not crystal clear on this, but it sure seems to work:


     harp:~ > cat a.rb
     class Duration
       def initialize seconds
         @seconds = seconds
       end
       def coerce other
         if other.class == @seconds.class
           [@seconds, other]
         else
           [Float(@seconds), Float(other)]
         end
       end
       def + other
         x, y = coerce(other)
         x + y
       end
     end
     def Duration(*a, &b); Duration::new(*a, &b); end

     p( 40 + Duration(2.0) )   # Float 42.0
     p( 40 + Duration(2) )     # Fixnum 42
     p( 40.0 + Duration(2.0) ) # Float 42.0
     p( 40.0 + Duration(2) )   # Float 42.0
     p( Duration(40) + 2 )     # Fixnum 42
     p( Duration(40) + 2.0 )   # Float 42.0
     p( Duration(40.0)  + 2)   # Float 42.0
     p( Duration(40.0) + 2.0 ) # Float 42.0


     harp:~ > ruby a.rb
     42.0
     42
     42.0
     42.0
     42
     42.0
     42.0
     42.0


i assume one would do something nice with the coerce method using Time,
Date,
and DateTime objects too...

regards.

-a
Markus (Guest)
on 2006-03-19 08:29
(Received via mailing list)
On Sat, 2006-03-18 at 17:11, removed_email_address@domain.invalid wrote:

>          else
>            [Float(@seconds), Float(other)]
>          end
>        end

There's a gottcha here.  Coerce is used to implement double dispatch and
so needs to return the coerced values in the opposite order (see Pick
Axe)--(other,self), not (self,other).

The way you wrote it works fine for addition & multiplication, but
you'll pull your hair out the first time you try to subtract or divide.

--MarkusQ

P.S. And I think (as Tom noted) that "to_f" was the answer to the
original question.
unknown (Guest)
on 2006-03-19 17:20
(Received via mailing list)
On Sun, 19 Mar 2006, Markus wrote:

>>          if other.class == @seconds.class
> The way you wrote it works fine for addition & multiplication, but
> you'll pull your hair out the first time you try to subtract or divide.

indeed.  good catch.

cheers.
-a
This topic is locked and can not be replied to.