Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unary operators behavior

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
like image 595
JKillian Avatar asked Feb 28 '14 03:02

JKillian


2 Answers

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.

like image 196
mu is too short Avatar answered Sep 23 '22 10:09

mu is too short


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]]]]]
like image 35
JKillian Avatar answered Sep 22 '22 10:09

JKillian