I've seen how to overload + and * in Ruby, so that
my_obj + other calls my_obj.+(other). In Python, you do this with __add__, and there's a corresponding __radd__ for overloading other + my_obj. Is there really no equivalent right-sided addition/multiplication in Ruby, and does that make it necessary to redefine + for each potential class of other?
In brief: say I have an object X which belongs to some new class defined by me. It's easy to write code for X + 5, but it seems that in order to handle 5 + X I'd need to redefine Fixnum.+. Is this true?
No, you don't need to redefine Fixnum#+ (or any other arithmetic method of ruby's numeric classes). The arithmetic methods of the numeric classes will call coerce if the two operands are not the same class. So if you define a coerce method for your class, 5 + instance_of_your_class will work fine without any changes to Fixnum#+.
Edit: Here's an example of using coerce:
class MyNum
attr_accessor :num
def initialize(num)
#num = num
end
def +(o)
lhs, rhs = coerce(o)
MyNum.new(lhs.num + rhs.num)
end
def coerce(o)
if o.is_a? MyNum
[self, o]
else
[self, MyNum.new(o)]
end
end
end
MyNum.new(5)+3 #=> #<MyNum:0x87fca08 #num=8>
3+MyNum.new(5) #=> #<MyNum:0x8807980 #num=8>
The RHS is coerced and needs to implement the to_i method which needs to return an appropriate value. The Numeric's classes implementation of to_int will end up calling it as part of the process of addition where other classes are involved on the RHS than strict Numeric descendants.
Yes. If you want to alter the behavior on 5 + x, you have to redefine + on whatever 5 is, since you're in fact calling 5.+(x) because of Ruby treating these kinds of operations as mechod calls.
Related
In Ruby the expression 1 + Complex(1,1) yields a Complex.
I would like to achieve the same for a user-defined type, so that (1 + MyType.new(...)) yields a value of MyType
Similarly for (1 - MyType.new(...)), but subtraction is not commutative.
Is this possible? How?
You can use coercion to handle this like so:
class MyType
attr_reader :value
def initialize(value)
#value = value
end
def +(n)
if n.is_a?(MyType)
MyType.new(self.value + n.value)
else
#value += n
self
end
end
def coerce(other)
[MyType.new(other), self]
end
end
1 + MyType.new(12)
#=> #<MyType:0x0000561609d5d9a8 #r=13>
# or even
1 + MyType.new(12) + 3
#=> #<MyType:0x0000561bc671d170 #r=16>
When the Integer 1 receives MyType as an argument to the + method it does not know how to handle it directly so it will try and coerce it to something it can understand by calling coerce on the argument object and passing itself to the coerce method.
The coerce method should always return an Array containing 2 values. Integer will then try and + those together using the first as the receiver and the second as the argument.
So essentially something like this:
a = 1
b = MyType.new(12)
begin
a + b
rescue TypeError
b.coerce(a).reduce(&:+)
end
This article offers a far more in-depth explanation of how the coercion works along with examples of when this would be useful.
When programming ruby I always find myself doing this:
a = [a, b].min
This means compare a and b and store the smallest value in a. I don't like writing the code above as I have to write a twice.
I know that some non-standard dialects of C++ had an operator which did exactly this
a <?= b
Which I find very convenient. But I'm not really interested in the operator as much as I'm in the feature of avoiding repetition. I would also be happy if I could write
a.keep_max(b)
a can be a quite long variable, like my_array[indice1][indice2], and you don't want to write that twice.
I did alot of googling on this and found no result, hopefully this question will pop up and be useful for others aswell.
So, is there any non-repeitive way to express what I want in ruby?
What you would like to do is in fact not possible in ruby (see this question). I think the best you can do is
def max(*args)
args.max
end
a = max a, b
I don't understand your question. You can always do something like this ...
module Comparable
def keep_min(other)
(self <=> other) <= 0 ? self : other
end
def keep_max(other)
(self <=> other) >= 0 ? self : other
end
end
1.keep_min(2)
=> 1
1.keep_max(2)
=> 2
Well, that won't work for all objects with <=> because not all of them are implementing Comparable, so you could monkey-patch Object.
Personally I prefer clarity and tend to avoid monkey-patching. Plus, this clearly is a binary predicate, just like "+", therefore method-chaining doesn't necessarily make sense so I prefer something like this to get rid of that array syntax:
def min(*args)
args.min
end
def max(*args)
args.max
end
min(1, 2)
=> 1
max(1, 2)
=> 2
But hey, I'm also a Python developer :-)
You can define your own method for it:
class Object
def keep_max(other)
[self, other].max
end
end
a = 3
b = 7
puts a.keep_max(b)
But you should be careful defining methods on Object as it can have unpredictable behaviour (for example, if objects cannot be compared).
def keep_max(var, other, binding)
eval "#{var} = [#{var}, #{other}].max", binding
end
a = 5
b = 78
keep_max(:a, :b, binding)
puts a
#=> 78
This basically does what you want. Take a look at Change variable passed in a method
It is said that when we have a class Point and knows how to perform point * 3 like the following:
class Point
def initialize(x,y)
#x, #y = x, y
end
def *(c)
Point.new(#x * c, #y * c)
end
end
point = Point.new(1,2)
p point
p point * 3
Output:
#<Point:0x336094 #x=1, #y=2>
#<Point:0x335fa4 #x=3, #y=6>
but then,
3 * point
is not understood:
Point can't be coerced into Fixnum (TypeError)
So we need to further define an instance method coerce:
class Point
def coerce(something)
[self, something]
end
end
p 3 * point
Output:
#<Point:0x3c45a88 #x=3, #y=6>
So it is said that 3 * point is the same as 3.*(point). That is, the instance method * takes an argument point and invoke on the object 3.
Now, since this method * doesn't know how to multiply a point, so
point.coerce(3)
will be called, and get back an array:
[point, 3]
and then * is once again applied to it, is that true?
Now, this is understood and we now have a new Point object, as performed by the instance method * of the Point class.
The question is:
Who invokes point.coerce(3)? Is it Ruby automatically, or is it some code inside of * method of Fixnum by catching an exception? Or is it by case statement that when it doesn't know one of the known types, then call coerce?
Does coerce always need to return an array of 2 elements? Can it be no array? Or can it be an array of 3 elements?
And is the rule that, the original operator (or method) * will then be invoked on element 0, with the argument of element 1? (Element 0 and element 1 are the two elements in that array returned by coerce.) Who does it? Is it done by Ruby or is it done by code in Fixnum? If it is done by code in Fixnum, then it is a "convention" that everybody follows when doing a coercion?
So could it be the code in * of Fixnum doing something like this:
class Fixnum
def *(something)
if (something.is_a? ...)
else if ... # other type / class
else if ... # other type / class
else
# it is not a type / class I know
array = something.coerce(self)
return array[0].*(array[1]) # or just return array[0] * array[1]
end
end
end
So it is really hard to add something to Fixnum's instance method coerce? It already has a lot of code in it and we can't just add a few lines to enhance it (but will we ever want to?)
The coerce in the Point class is quite generic and it works with * or + because they are transitive. What if it is not transitive, such as if we define Point minus Fixnum to be:
point = Point.new(100,100)
point - 20 #=> (80,80)
20 - point #=> (-80,-80)
Short answer: check out how Matrix is doing it.
The idea is that coerce returns [equivalent_something, equivalent_self], where equivalent_something is an object basically equivalent to something but that knows how to do operations on your Point class. In the Matrix lib, we construct a Matrix::Scalar from any Numeric object, and that class knows how to perform operations on Matrix and Vector.
To address your points:
Yes, it is Ruby directly (check calls to rb_num_coerce_bin in the source), although your own types should do too if you want your code to be extensible by others. For example if your Point#* is passed an argument it doesn't recognize, you would ask that argument to coerce itself to a Point by calling arg.coerce(self).
Yes, it has to be an Array of 2 elements, such that b_equiv, a_equiv = a.coerce(b)
Yes. Ruby does it for builtin types, and you should too on your own custom types if you want to be extensible:
def *(arg)
if (arg is not recognized)
self_equiv, arg_equiv = arg.coerce(self)
self_equiv * arg_equiv
end
end
The idea is that you shouldn't modify Fixnum#*. If it doesn't know what to do, for example because the argument is a Point, then it will ask you by calling Point#coerce.
Transitivity (or actually commutativity) is not necessary, because the operator is always called in the right order. It's only the call to coerce which temporarily reverts the received and the argument. There is no builtin mechanism that insures commutativity of operators like +, ==, etc...
If someone can come up with a terse, precise and clear description to improve the official documentation, leave a comment!
I find myself often writing code along this pattern when dealing with commutativity:
class Foo
def initiate(some_state)
#...
end
def /(n)
# code that handles Foo/n
end
def *(n)
# code that handles Foo * n
end
def coerce(n)
[ReverseFoo.new(some_state),n]
end
end
class ReverseFoo < Foo
def /(n)
# code that handles n/Foo
end
# * commutes, and can be inherited from Foo
end
In Ruby, it seems that a lot of coerce() help can be done by
def coerce(something)
[self, something]
end
that's is, when
3 + rational
is needed, Fixnum 3 doesn't know how to handle adding a Rational, so it asks Rational#coerce for help by calling rational.coerce(3), and this coerce instance method will tell the caller:
# I know how to handle rational + something, so I will return you the following:
[self, something]
# so that now you can invoke + on me, and I will deal with Fixnum to get an answer
So what if most operators can use this method, but not when it is (a - b) != (b - a) situation? Can coerce() know which operator it is, and just handle those special cases, while just using the simple [self, something] to handle all the other cases where (a op b) == (b op a) ? (op is the operator).
The point of coerce is not to know what operation you are trying to perform. Its purpose is to bring the argument and self to a common ground. Additionally, same operators can be commutative in certain classes, and not in other (Numeric#+ and Array#+, for example), so your small commutativity-based coerce exploit really won't pay off.
Instead of pushing your coerce to do what it's not intended to, you should create a new class instead (such as ScalarPoint, for example), and use it to interface scalar values with your Point:
class ScalarPoint
attr_reader :val
def initialize(val)
#val = val
end
def +(arg)
case arg
when Point:
Point.new(#val + arg.x, #val + arg.y)
when ScalarPoint:
ScalarPoint.new(arg.val + #val)
else
raise "Unknown operand!"
end
end
# define other operators, such as * etc
def coerce(arg)
return [ScalarPoint.new(arg), self] if arg.is_a?(Numeric)
raise "Can't handle"
end
end
and
class Point
def coerce(arg)
[ScalarPoint.new(arg), self] if arg.is_a?(Numeric)
raise "Can't handle"
end
end
etc. (NB: code not tested)
The answer to this question is that you can know the operator by looking at the backtrace but you shouldn't do that.
That is not how the coerce mechanism of Ruby has been designed. As I answered in your previous question, coerce should return two equivalent values [a, b] such that a.send(operator, b) will work, whatever the operator.
i've been messing around with ruby and opengl for entertainment purposes, and i decided to write some 3d vector/plane/etc classes to pretty up some of the math.
simplified example:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(#x*a, #y*a, #z*a)
elsif a.is_a?(Vec3) #dot product
return #x*a.x + #y*a.y + #z*a.z
end
end
end
v1 = Vec3.new(1,1,1)
v2 = v1*5 #produces [5,5,5]
which all fine and dandy, but i also want to be able to write
v2 = 5*v1
which requires adding functionality to Fixnum or Float or whatever, but i couldn't find a way to overload or extend fixnum's multiplication without replacing it entirely. is this possible in ruby? any tips?
(obviously i can just write all my multiplications in the correct order if i need to)
Using coerce is a MUCH better approach than monkey-patching a core class:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(#x*a, #y*a, #z*a)
elsif a.is_a?(Vec3) #dot product
return #x*a.x + #y*a.y + #z*a.z
end
end
def coerce(other)
return self, other
end
end
if you define v as v = Vec3.new then the following will work: v * 5 and 5 * v
The first element returned by coerce (self) becomes the new receiver for the operation, and the second element (other) becomes the parameter, so 5 * v is exactly equivalent to v * 5
I believe the following will do what you want, though banister's suggestion to use coerce instead of monkey-patching Numeric is a preferred method. Use this method only if necessary (for example if you only want some binary operands to be transitive).
Fixnum.class_eval do
original_times = instance_method(:*)
define_method(:*) do |other|
if other.kind_of?(Vec3)
return other * self
else
return original_times.bind(self).call(other)
end
end
end