Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is unary minus supposed to have higher precedence for numeric literals?

Tags:

ruby

Unary minus seems to have special precedence when a numeric literal is involved. Is this documented behavior?

The precedence tables I've seen (e.g. here) don't even mention the dot (method-call) operator.

Test on ruby 2.3.6:

puts "=== literal integer ==="
# `-` has higher precedence than `.`
p( -1.abs )   # => 1
p( -(1.abs) ) # => -1 (previous line should match this if `.` had higher precedence)

puts "=== literal float ==="
# again `-` has higher precedence than `.`
p( -1.2.abs )   # => 1.2
p( -(1.2.abs) ) # => -1.2 (previous line should match this if `.` had higher precedence)

puts "=== integer in a variable ==="
(1).tap do |i|
  # `.` has higher precedence
  p( -i.abs )   # -1
  p( (-i).abs ) # 1 (previous line should match this if `-` had higher precedence)
end

puts "=== float in a variable ==="
(1.2).tap do |i|
  # `.` has higher precedence
  p( -i.abs )   # -1.2
  p( (-i).abs ) # 1.2 (previous line should match this if `-` had higher precedence)
end

puts "=== literal string ==="
'a'.frozen? == false or raise "frozen_string_literal must be disabled"

# Note that unary minus on Strings returns a frozen copy if the string wasn't already frozen
# `.` has higher precedence (differs from numeric precedence)
p( (-'a'.succ).frozen? )   # true
p( ((-'a').succ).frozen? ) # false (previous line should match this if `-` had higher precedence)
like image 930
Kelvin Avatar asked Oct 25 '25 12:10

Kelvin


1 Answers

The reason is that in the literal number case, the - in front isn't an unary operator, but part of the literal syntax.

However, the - operator itself has lower precedence than method invocation. Given that there is no -'string' literal syntax for strings, this rule always applies regardless of if the string was literal or not.

class Integer
  def -@
    puts 'Called'
  end
end

class String
  def -@
    puts 'Called'
  end
end

-1 # nothing, the - wasn't an unary operation, but part of the number construction
x = 1
-x # Called

-'a' # Called
a = 'a'
-a # Called

Another interesting thing is that if you put a space between the number and the -, the - is no longer part of the literal syntax.

- 1 # Called

Here is the semantic explanation:

  • There is such thing as "the number negative one". It makes sense that there should be literal syntax for it (as there is for any positive number). And the most intuitive syntax for it is -1.
  • We still want to be able to call the unary operator on literal positive numbers. The most intuitive (and easy to implement) way for that would be not to make the parser super fancy as to ignore any random amount of whitespaces in the literal syntax for negative numbers. Hence why - 1 accounts to "apply the unary minus to the number (positive) one".
  • There is no such thing as "the string negative 'a'". That is why -'a' means "apply the unary minus to the string 'a'".
like image 188
ndnenkov Avatar answered Oct 27 '25 02:10

ndnenkov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!