From: Rick DeNatale Date: 2010-05-13T02:13:57+09:00 Subject: Re: In Ruby, can the coerce() method know what operator it is th On Wed, May 12, 2010 at 12:16 PM, Colin Bartlett wrote: > On Tue, May 11, 2010 at 4:46 PM, Caleb Clausen wrote: > ...[for coerce] >> Generally, you want to return an array containing (a representation of) other first >> and then (a representation of) self. > I've been wondering how to express concisely what you've concisely > expressed there! > (I'm referring to the "(a representation of)" bit for both other and self.) > >> That helps ensure that when the caller calls >>    other.op self >> it won't end up with the arguments backwards. >> So operators like - and / can work correctly. >> If you return self first and then other, + and * will work but, not - and /. > With a pedantic former maths student's hat on: > If you return self first and then other: >  + will (probably?) work because (almost?) all uses of + are commutative >   (I've never seen a non-commutative use of +, but you never know!); >  * will probably work, but may not if the use of * is not commutative >   (for example matrix multiplication?); >  - and / and ** (probably?) won't work because (almost?) all uses of them >   are non-commutative. (I've never seen commutative uses, but ...); >  and if <=> is defined for (a representation of) other and >    (a representation of) self, that probably won't work either! Based on an earlier thread from the OP where the use case was operations on integers and points, he might be concerned about my pointing out (no pun intended) that, as far as I know, while there's a conventional meaning for multiplying a vector quantity (like a Point) and a scalar quantity (like an Integer), there isn't for adding or subtracting a scalar and a vector. So we need to allow 1 * Point.new(1,1) but disallow 1 + Point.new(1, 1) or 1 - Point.new(1,1) One way to address this might be something like class Point attribute :x, :y def initialize(x, y) self.x, self.y = x, y end def +(value) if Point === value Point.new(x + value.x, y + value.y) else raise ArgumentError.new("Attempt to add a point to a scalar") end end def -(value) if Point === value Point.new(x + value.x, y + value.y) else raise ArgumentError.new("Attempt to subtract a point from a scalar") end end def dot_product(other_point) x*other_point.x + y * other_point.y end def *(value) if Point == value dot_product(value) else Point.new(x*value, y*value) end end class ScalarWrapper attribute :scalar_value def initialize(scalar_value) self.scalar_value = scalar_value end def +(value) raise ArgumentError.new("Attempt to add scalar to a point") end def -(value) raise ArgumentError.new("Attempt to subtract a scalar from a point") end def *(other_point) Point.new(scalar_value*other_point.x, scalar_value*other_point.x) end end end def coerce(other) [Point::ScalarWrapper.new(other), self] end end -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Github: http://github.com/rubyredrick Twitter: @RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale