# Coercion in Ruby

**URL:** <https://rubytalk.org/t/coercion-in-ruby/61592>\
**Category:** ruby-talk\
**Created:** [26 January 2011 14:58 UTC](https://rubytalk.org/t/coercion-in-ruby/61592 "2011-01-26T14:58:15Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Zach\_Dennis](https://avatars.discourse-cdn.com/v4/letter/z/3d9bf3/32.png) [@Zach\_Dennis](https://rubytalk.org/u/Zach_Dennis)\
**Post date:** [26 January 2011 14:58 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/1 "2011-01-26T14:58:15Z")

</div>

A fellow rubyist, Zach Church, wrote a great article on coercion in  
Ruby. It's not something I've seen covered often so I thought I'd  
share with the community at large:

> **[Class Coercion in Ruby](https://www.mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby)**
>
> I often run into situations where I'm working with something that's essentially a number, but I want it to behave just a little differently. For example, in triathlon training I often deal with time intervals. I may be repeating a certain swimming...

Zach Dennis

---

<div class="post-metadata">

**Author:** ![Josh\_Cheek](https://avatars.discourse-cdn.com/v4/letter/j/e79b87/32.png) [@Josh\_Cheek](https://rubytalk.org/u/Josh_Cheek)\
**Post date:** [26 January 2011 15:18 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/2 "2011-01-26T15:18:40Z")

</div>

Thanks for sharing, very well done.

> **···**
>
> On Wed, Jan 26, 2011 at 8:58 AM, zdennis \<zach.dennis@gmail.com\> wrote:
> 
> > A fellow rubyist, Zach Church, wrote a great article on coercion in  
> > Ruby. It's not something I've seen covered often so I thought I'd  
> > share with the community at large:
> > 
> > [http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby](http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby)
> > 
> > Zach Dennis

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [26 January 2011 15:29 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/3 "2011-01-26T15:29:54Z")

</div>

In case you weren't aware: I covered this thoroughly a while ago when  
I described what it needs to create a numeric class which nicely plays  
along with other numeric classes.

[http://blog.rubybestpractices.com/posts/rklemme/019-Complete\_Numeric\_Class.html](http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html)

Cheers

robert

> **···**
>
> On Wed, Jan 26, 2011 at 3:58 PM, zdennis \<zach.dennis@gmail.com\> wrote:
> 
> > A fellow rubyist, Zach Church, wrote a great article on coercion in  
> > Ruby. It's not something I've seen covered often so I thought I'd  
> > share with the community at large:
> > 
> > [http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby](http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby)
> 
> --  
> remember.guy do |as, often| as.you\_can - without end  
> [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)

---

<div class="post-metadata">

**Author:** ![Tony\_Arcieri4](https://avatars.discourse-cdn.com/v4/letter/t/c77e96/32.png) [@Tony\_Arcieri4](https://rubytalk.org/u/Tony_Arcieri4)\
**Post date:** [26 January 2011 20:52 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/4 "2011-01-26T20:52:12Z")

</div>

Just a small nitpick:

&nbsp;&nbsp;if other.is\_a? TimeInterval  
&nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other.value)  
&nbsp;&nbsp;elsif other.is\_a? Numeric  
&nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other)  
&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
&nbsp;&nbsp;end

would probably be better written as:

&nbsp;&nbsp;case other  
&nbsp;&nbsp;when TimeInterval  
&nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other.value)  
&nbsp;&nbsp;when Numeric  
&nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other)  
&nbsp;&nbsp;else raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
&nbsp;&nbsp;end

> **···**
>
> On Wed, Jan 26, 2011 at 7:58 AM, zdennis \<zach.dennis@gmail.com\> wrote:
> 
> > A fellow rubyist, Zach Church, wrote a great article on coercion in  
> > Ruby. It's not something I've seen covered often so I thought I'd  
> > share with the community at large:
> > 
> > [http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby](http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby)
> > 
> > Zach Dennis
> 
> --  
> Tony Arcieri  
> Medioh! Kudelski

---

<div class="post-metadata">

**Author:** ![Zach\_Dennis](https://avatars.discourse-cdn.com/v4/letter/z/3d9bf3/32.png) [@Zach\_Dennis](https://rubytalk.org/u/Zach_Dennis)\
**Post date:** [26 January 2011 15:40 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/5 "2011-01-26T15:40:05Z")

</div>

Thanks Robert, I hadn't seen your post before. Will add to my reader docket!

Zach

> **···**
>
> On Wed, Jan 26, 2011 at 10:29 AM, Robert Klemme \<shortcutter@googlemail.com\> wrote:
> 
> > On Wed, Jan 26, 2011 at 3:58 PM, zdennis \<zach.dennis@gmail.com\> wrote:
> > 
> > > A fellow rubyist, Zach Church, wrote a great article on coercion in  
> > > Ruby. It's not something I've seen covered often so I thought I'd  
> > > share with the community at large:
> > > 
> > > [http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby](http://mutuallyhuman.com/blog/2011/01/25/class-coercion-in-ruby)
> > 
> > In case you weren't aware: I covered this thoroughly a while ago when  
> > I described what it needs to create a numeric class which nicely plays  
> > along with other numeric classes.
> > 
> > [http://blog.rubybestpractices.com/posts/rklemme/019-Complete\_Numeric\_Class.html](http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html)
> > 
> > Cheers
> > 
> > robert
> > 
> > --  
> > remember.guy do |as, often| as.you\_can - without end  
> > [http://blog.rubybestpractices.com/](http://blog.rubybestpractices.com/)
> 
> --  
> Zach Dennis  
> [http://www.continuousthinking.com](http://www.continuousthinking.com) (personal)  
> [http://www.mutuallyhuman.com](http://www.mutuallyhuman.com) (hire me)  
> [http://ideafoundry.info/behavior-driven-development](http://ideafoundry.info/behavior-driven-development) (first rate BDD training)  
> @zachdennis (twitter)

---

<div class="post-metadata">

**Author:** ![David\_Masover](https://avatars.discourse-cdn.com/v4/letter/d/50afbb/32.png) [@David\_Masover](https://rubytalk.org/u/David_Masover)\
**Post date:** [27 January 2011 04:20 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/6 "2011-01-27T04:20:35Z")

</div>

Or at least:

if other.kind\_of? TimeInterval  
&nbsp;&nbsp;...  
elsif other.kind\_of? Numeric  
&nbsp;&nbsp;...

Remember duck typing? I don't care if it is\_a Numeric, not really. I can't  
really think of many cases where is\_a? makes more sense than kind\_of?.

> **···**
>
> On Wednesday, January 26, 2011 02:52:12 pm Tony Arcieri wrote:
> 
> > Just a small nitpick:
> > 
> > &nbsp;&nbsp;if other.is\_a? TimeInterval  
> > &nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other.value)  
> > &nbsp;&nbsp;elsif other.is\_a? Numeric  
> > &nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other)  
> > &nbsp;&nbsp;else  
> > &nbsp;&nbsp;&nbsp;&nbsp;raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
> > &nbsp;&nbsp;end
> > 
> > would probably be better written as:
> > 
> > &nbsp;&nbsp;case other  
> > &nbsp;&nbsp;when TimeInterval  
> > &nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other.value)  
> > &nbsp;&nbsp;when Numeric  
> > &nbsp;&nbsp;&nbsp;&nbsp;TimeInterval.new(value + other)  
> > &nbsp;&nbsp;else raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
> > &nbsp;&nbsp;end

---

<div class="post-metadata">

**Author:** ![Tony\_Arcieri4](https://avatars.discourse-cdn.com/v4/letter/t/c77e96/32.png) [@Tony\_Arcieri4](https://rubytalk.org/u/Tony_Arcieri4)\
**Post date:** [27 January 2011 04:52 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/7 "2011-01-27T04:52:25Z")

</div>

Good point, but that said, it can be done even better:

First, in TimeInterval, alias\_method :to\_int, :value

Then when implementing +:

if other.respond\_to? :to\_int  
&nbsp;&nbsp;TimeInterval.new(value + other.to\_int)  
elsif other.respond\_to? :coerce  
&nbsp;&nbsp;a, b = other.coerce(self)  
&nbsp;&nbsp;a + b  
else raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
end

> **···**
>
> On Wed, Jan 26, 2011 at 9:20 PM, David Masover \<ninja@slaphack.com\> wrote:
> 
> > Or at least:
> > 
> > if other.kind\_of? TimeInterval  
> > ...  
> > elsif other.kind\_of? Numeric  
> > ...
> > 
> > Remember duck typing?
> 
> --  
> Tony Arcieri  
> Medioh! Kudelski

---

<div class="post-metadata">

**Author:** ![Adam\_Prescott1](https://avatars.discourse-cdn.com/v4/letter/a/2acd7d/32.png) [@Adam\_Prescott1](https://rubytalk.org/u/Adam_Prescott1)\
**Post date:** [27 January 2011 16:19 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/8 "2011-01-27T16:19:18Z")

</div>

Am I missing something here? I don't see how this is in keeping with the  
idea of duck typing: is\_a? is an alias for kind\_of? and vice-versa.

[http://www.ruby-doc.org/core/classes/Object.html#M001034](http://www.ruby-doc.org/core/classes/Object.html#M001034)

Perhaps you were thinking of instance\_of? here?

> **···**
>
> On Thu, Jan 27, 2011 at 4:20 AM, David Masover \<ninja@slaphack.com\> wrote:
> 
> > On Wednesday, January 26, 2011 02:52:12 pm Tony Arcieri wrote:  
> > \> Just a small nitpick:  
> > \>  
> > \> if other.is\_a? TimeInterval  
> > \> TimeInterval.new(value + other.value)  
> > \> elsif other.is\_a? Numeric  
> > \> TimeInterval.new(value + other)  
> > \> else  
> > \> raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
> > \> end  
> > \>  
> > \> would probably be better written as:  
> > \>  
> > \> case other  
> > \> when TimeInterval  
> > \> TimeInterval.new(value + other.value)  
> > \> when Numeric  
> > \> TimeInterval.new(value + other)  
> > \> else raise TypeError, "#{other.class} can't be coerced into  
> > TimeInterval"  
> > \> end
> > 
> > Or at least:
> > 
> > if other.kind\_of? TimeInterval  
> > ...  
> > elsif other.kind\_of? Numeric  
> > ...
> > 
> > Remember duck typing? I don't care if it is\_a Numeric, not really. I can't  
> > really think of many cases where is\_a? makes more sense than kind\_of?.

---

<div class="post-metadata">

**Author:** ![David\_Masover](https://avatars.discourse-cdn.com/v4/letter/d/50afbb/32.png) [@David\_Masover](https://rubytalk.org/u/David_Masover)\
**Post date:** [27 January 2011 05:37 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/9 "2011-01-27T05:37:36Z")

</div>

Disclaimer: The length of this post in particular has nothing whatsoever to do  
with how strong or well-informed I am about the topic. I actually didn't know  
about coercion until today, so I may be missing something obvious. Proceed  
with caution...

> \> Or at least:  
> \>  
> \> if other.kind\_of? TimeInterval  
> \>  
> \> ...  
> \>  
> \> elsif other.kind\_of? Numeric  
> \>  
> \> ...  
> \>  
> \> Remember duck typing?
> 
> Good point,

Well, to a point. I just reflexively change is\_a? to kind\_of? everywhere I see  
it, unless there's a good reason not to. I don't have a problem with your  
cases, just pointing out an alternative.

> but that said, it can be done even better:
> 
> First, in TimeInterval, alias\_method :to\_int, :value

If this article is correct:

[http://www.rubyfleebie.com/to\_i-vs-to\_int/](http://www.rubyfleebie.com/to_i-vs-to_int/)

...then I'm not sure that's the case. Do we always want to represent a time  
interval as an integer number of seconds? Maybe, but my instinct is that maybe  
there are other integers we could get out of this object.

> if other.respond\_to? :to\_int  
> &nbsp;&nbsp;TimeInterval.new(value + other.to\_int)  
> elsif other.respond\_to? :coerce  
> &nbsp;&nbsp;a, b = other.coerce(self)  
> &nbsp;&nbsp;a + b  
> else raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
> end

While I agree that to\_int is the correct way to check if something's an  
integer, the original check was for anything numeric, so it might not be  
sufficient (though it's probably fine for this class so far).

So maybe something like:

if other.respond\_to?(:to\_int) || other.kind\_of?(Numeric)  
&nbsp;&nbsp;TimeInterval.new(value + other.to\_int)  
elsif other.respond\_to? :coerce  
&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;a, b = other.coerce(self)  
&nbsp;&nbsp;&nbsp;&nbsp;a + b  
&nbsp;&nbsp;rescue TypeError  
&nbsp;&nbsp;&nbsp;&nbsp;if other.respond\_to?(:to\_i)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self + other.to\_i  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
elsif other.respond\_to?(:to\_i)  
&nbsp;&nbsp;self + other.to\_i  
else  
&nbsp;&nbsp;raise TypeError, "#{other.class} can't be coerced into TimeInterval"  
end

That could probably be DRYed up a bit.

The idea is, if something has a to\_int or is a Numeric of some sort, it's  
claiming that it is \_just\_ a number, so we're probably free to treat it as  
such. Otherwise, let it try to coerce first before we fall back to tricks like  
to\_i.

Why?

It's a contrived example to begin with, so my answer will be just as  
contrived, but... Suppose I create, say, an HourInterval class, which  
represents time intervals of hours. Then:

HourInterval.new(5)

You'd expect that to be five hours, right? Then you'd also expect:

HourInterval.new(5).to\_i

...that should return 5. However, if we just let it to\_i (or to\_int, if it's  
pretending to be just a number), then TimeInterval will treat it as five  
\_seconds\_ instead of five hours, no matter what we do. That's why I'd suggest  
giving it a chance to coerce first, and saving the fuzziest conversions for  
when that fails.

Now the only trick is to make sure that we never call 'coerce' from inside a  
'coerce' method, unless I'm missing something.

I'm told exception handling is expensive. I wonder if it might make sense to  
have a non-raising form of 'coerce', or a can\_coerce? method, to avoid that  
overhead. But then, if you're doing this sort of thing and you start caring  
about performance, you can always manually convert things ahead of time.

> **···**
>
> On Wednesday, January 26, 2011 10:52:25 pm Tony Arcieri wrote:
> 
> > On Wed, Jan 26, 2011 at 9:20 PM, David Masover \<ninja@slaphack.com\> wrote:

---

<div class="post-metadata">

**Author:** ![David\_Masover](https://avatars.discourse-cdn.com/v4/letter/d/50afbb/32.png) [@David\_Masover](https://rubytalk.org/u/David_Masover)\
**Post date:** [27 January 2011 18:22 UTC](https://rubytalk.org/t/coercion-in-ruby/61592/10 "2011-01-27T18:22:38Z")

</div>

Apparently so. Sorry for the noise...

> **···**
>
> On Thursday, January 27, 2011 10:19:18 am Adam Prescott wrote:
> 
> > On Thu, Jan 27, 2011 at 4:20 AM, David Masover \<ninja@slaphack.com\> wrote:  
> > \> Remember duck typing? I don't care if it is\_a Numeric, not really. I  
> > \> can't really think of many cases where is\_a? makes more sense than  
> > \> kind\_of?.
> > 
> > Am I missing something here? I don't see how this is in keeping with the  
> > idea of duck typing: is\_a? is an alias for kind\_of? and vice-versa.
> > 
> > [class Object - RDoc Documentation](http://www.ruby-doc.org/core/classes/Object.html#M001034)
> > 
> > Perhaps you were thinking of instance\_of? here?
