Had some weird results when redefining the unary +
operator in Ruby on the Fixnum
class. Not exactly sure why things are happening the way they are (specifically line 009
).
irb:003> class Fixnum
irb:004> def +@ #define unary +
irb:005> 15
irb:006> end
irb:007> end
=> nil
irb:008> 2
=> 2
irb:009> +2
=> 2
irb:010> +(2)
=> 15
irb:011> ++2
=> 15
I suspect you're seeing a side effect of the parser's behavior in how it interprets numeric literals.
If we create our own class:
class C
def +@
11
end
end
and then look at some things:
> c = C.new
> +c
=> 11
> ++c
=> 11
That's exactly what we expect to happen. If we use your Fixnum
unary +
override and a Fixnum
variable:
> n = 23
> +n
=> 15
> ++n
=> 15
then again we see what you're expecting. In both cases, we see the result of calling the +@
method on a non-literal.
But when we look at +6
with your operator in place:
> +6
=> 6
the +@
method is not called. Similarly if we override -@
:
class Fixnum
def -@
'pancakes'
end
end
and see what it does:
> -42
=> 42
So what's going on here? Well, Ruby is seeing +6
and -42
not as 6.send(:+@)
and 42.send(:-@)
method calls but as single literals for positive six and negative forty-two.
If you start adding parentheses, +(6)
and -(42)
, then Ruby sees non-literal expressions and ends up calling the unary methods. Similarly when you double the unary operators.
This answer adds additional details to mu's answer.
Just learned about ruby's Ripper
class, and it shows what's happening pretty clearly:
require 'ripper'
p Ripper.sexp('2') # => [:program, [[:@int, "2", [1, 0]]]]
p Ripper.sexp('+2') # => [:program, [[:@int, "+2", [1, 0]]]]
p Ripper.sexp('+(2)') # => [:program, [[:unary, :+@, [:paren, [[:@int, "2", [1, 2]]]]]]]
p Ripper.sexp('++2') # => [:program, [[:unary, :+@, [:@int, "+2", [1, 1]]]]]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With