Skip to content

Operators

datanorris edited this page Feb 6, 2016 · 25 revisions

Class-defined operators

Most of the operators in Ruby have behaviour defined by the class of the object they are applied to, when the class implements the operator's method.

Specifically:

  • For unary operators, the operator's method is invoked on the operand e.g. !a is equivalent to a.!()
  • For binary operators, the operator's method is invoked on the left operand with a single argument of the right operand, e.g. a + b is equivalent to a.+(b)

Because operator behaviour is defined by API, not language, I won't try to describe them exhaustively here, but I will give an overview of their behaviour for core classes.

Unary operators

Unary minus

- <value>

Implements <value>.-@()

Standard unary minus operator.

Class Action
Numeric Returns 0 - <value>
Fixnum Returns negation of <value>
Float Returns negation of <value>
Bignum Returns negation of <value>
Complex Returns negation of <value>

Unary plus

+ <value>

Implements <value>.+@()

Standard unary plus operator.

Class Action
Numeric Returns <value>

Unary complement

~ <value>

Implements <value>.~()

For integers, a standard complement operator. For regular expressions, performs a match to the last line.

Class Action
Fixnum Returns one's complement of <value>
Bignum Returns one's complement of <value> (bignums are conceptually of infinite length)
Regexp Returns the default implementation of <value> =~ $_

Unary not

! <value>
not <value>

Implements <value>.!()

A standard logical "not" operator.

Class Action
Object Returns true if <value> is falsy, otherwise false

Binary arithmetic operators

Binary plus

<left-value> + <right-value>

Implements <left-value>.+(<right_value>)

Standard addition operator for Numerics. Standard concatenation operator for Arrays and Strings. Add seconds for Time.

Class Action
Fixnum, Bignum, Rational, Float, Complex Returns the addition of left and right Numeric values. Can add 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here)
Array Returns the concatenation of the left and right Array values. Right operand can be implicitly converted with to_ary
String Returns the concatenation of the left and right String values. Right operand can be implicitly converted with to_str
Time Returns a Time object resulting in adding the Numeric right value, in seconds, to the Time object

Binary minus

<left-value> - <right-value>

Implements <left-value>.-(<right_value>)

Standard subtraction operator for Numerics. For Arrays, returns an Array containing the left value's items excluding the right value's items. For Time, subtract another Time and get # seconds, or subtract # seconds and get a Time.

Class Action
Fixnum, Bignum, Rational, Float, Complex Returns the subtraction of left and right Numeric values. Can subtract 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here)
Array Returns an Array containing the values in the left operand excluding the values in the right operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary
Time If right value is Numeric, returns a Time object resulting in subtracting that value, in seconds, from the Time object. If right value is Time, returns the number of seconds between the 2 times.

Binary multiply

<left-value> * <right-value>

Implements <left-value>.*(<right_value>)

Standard multiplication operator for Numerics. For Arrays and Strings, concatenates the specified number of copies of the left operand. For arrays can also be used to join elements into a string separated by right operand.

Class Action
Fixnum, Bignum, Rational, Float, Complex Returns the multiplication of left and right Numeric values. Can multiply 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here)
Array If right value is a String, will return a String containing each of the Array elements (stringified) separated by the string in the right value. If right value is a Numeric, will return an Array that is the concatenation of that many copies of the left value.
String Right value is a Numeric, returns a String that is the concatenation of that many copies of the left value.

Binary divide

<left-value> / <right-value>

Implements <left-value>./(<right_value>)

Standard division operator for Numerics.

Class Action
Fixnum, Bignum, Rational, Float, Complex Returns the division of left and right Numeric values. Can divide 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here)

Binary modulus

<left-value> % <right-value>

Implements <left-value>.%(<right_value>)

Standard modulus operator for Numerics (except Complex). For Strings, acts like sprintf with the left value being the format string and the right value being arguments.

Class Action
Fixnum, Bignum, Rational, Float Returns the modulus of left and right Numeric values. Can modulo 2 values each of any Numeric class, and it will "upgrade" one of the operands to match the class of the other (in the order listed here)
String Returns sprintf(<left-value>, *<right-value>)

Binary power

<left-value> ** <right-value>

Implements <left-value>.**(<right_value>)

Standard power operator for Numerics.

Class Action
Fixnum, Bignum, Rational, Float, Complex Returns the power of left and right Numeric values

Binary bitwise and

<left-value> & <right-value>

Implements <left-value>.&(<right_value>)

Standard bitwise and operator for Integers. For true/false/nil, behaves as logical and except without short circuit evaluation and always returning true or false. For Arrays, behaves as intersection operator.

Class Action
Fixnum, Bignum Returns the bitwise and of left and right Integer values, upgrading a Fixnum to a Bignum if necessary
true, false, nil Returns <left-value> && <right-value> except will always evaluate both arguments and return true or false
Array Returns an Array containing the values in the left operand excluding any values not in the right operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary

Binary bitwise or

<left-value> | <right-value>

Implements <left-value>.|(<right_value>)

Standard bitwise or operator for Integers. For true/false/nil, behaves as logical or except without short circuit evaluation and always returning true or false. For Arrays, behaves as union operator.

Class Action
Fixnum, Bignum Returns the bitwise or of left and right Integer values, upgrading a Fixnum to a Bignum if necessary
true, false, nil Returns `
Array Returns an Array containing the values in the left operand with values in the right operand appended except for values already in the left operand. Matching elements are located by converting one Array to a Hash and testing each element in the other Array against it. Right operand can be implicitly converted with to_ary

Binary bitwise xor

<left-value> ^ <right-value>

Implements <left-value>.^(<right_value>)

Standard bitwise exclusive or operator for Integers. For true/false/nil, behaves as logical exclusive or except without short circuit evaluation and always returning true or false.

Class Action
Fixnum, Bignum Returns the bitwise xor of left and right Integer values, upgrading a Fixnum to a Bignum if necessary
true, false, nil Returns true if either <left-value> or <right-value> is truthy, but not both, otherwise returns false, again without short circuit evaluation

Binary left shift

<left-value> << <right-value>

Implements <left-value>.<<(<right_value>)

Standard bitwise left shift operator for Integers. For Arrays, modifies the left value by appending the right value, and returns the left value. For Strings, concatenates the right value (either a String or an Integer codepoint) onto the left value and returns the left value.

Class Action
Fixnum, Bignum Returns the left value bitwise left-shifted by the right Integer value, upgrading to a Bignum if necessary. If right value is negative, treated as right shift
Array Returns the left value after appending the right value to it
String Concatenates the right value (either a String or an Integer codepoint) onto the left value and returns the left value

Binary right shift

<left-value> >> <right-value>

Implements <left-value>.>>(<right_value>)

Standard bitwise right shift operator for Integers.

Class Action
Fixnum, Bignum Returns the left value bitwise right-shifted by the right value. If right value is negative, treated as left shift

Match operators

Binary match

<left-value> =~ <right-value>

Implements <left-value>.=~(<right-value>)

For Regexp, implements a regular expression match, returning the match position. For String and Symbol, tries the match with the operands switched around. For Kernel (i.e. default implementation), returns nil.

Class Action
Kernel returns nil
Regexp Performs a regular expression match to right value, returns nil if failed, otherwise the position in the right value where the match was found (beginning from 0)
String, Symbol If the right value is a String, raise error. If it's a Regexp, return the default implementation of <right-value> =~ <left-value>, otherwise return the current implementation of <right-value> =~ <left-value>

Binary not-match

<left-value> !~ <right-value>

Implements <left-value>.!~(<right-value>)

Tests for whether <left-value> =~ <right-value> fails.

Class Action
Kernel returns false if <left-value> =~ <right-value> is truthy, otherwise true

Comparison operators

With the exception of <=>, comparison operators are expected to return a truthy value if comparison is true, a falsy value if comparison false, and perhaps nil if the operands are not comparable.

Binary greater-than

<left-value> > <right-value>

Implements <left-value>.>(<right-value>)

Standard greater-than operator for Numerics (except Complex) and Time. For Modules, is-superclass-of operator. For Strings and Symbols, standard string-style greater than operator.

Class Action
Fixnum, Bignum, Float, Rational Returns true if left greater than right, otherwise false
Time Returns true if left greater than right, otherwise false
Module Returns true if left is a superclass of right, false if it's the same or a subclass, or nil if it's not related
String, Symbol Returns true if the strings are not equal and the first non-matching character in left has a codepoint greater than the character in right (or if left string is longer)

Binary less-than

<left-value> < <right-value>

Implements <left-value>.<(<right-value>)

Standard less-than operator, like inverse of greater-than (except matching values still return false).

Class Action
Fixnum, Bignum, Float, Rational Returns true if left less than right, otherwise false
Time Returns true if left less than right, otherwise false
Module Returns true if left is a subclass of right, false if it's the same or a superclass, or nil if it's not related
String, Symbol Returns true if the strings are not equal and the first non-matching character in left has a codepoint less than the character in right (or if left string is shorter)

Binary greater-than-or-equals

<left-value> >= <right-value>

Implements <left-value>.>=(<right-value>)

Standard greater-than-or-equals operator, like inverse of less-than.

Class Action
Fixnum, Bignum, Float, Rational Returns true if left greater than or equal to right, otherwise false
Time Returns true if left greater than or equal to right, otherwise false
Module Returns true if left is the same or a superclass of right, false if it's a subclass, or nil if it's not related
String, Symbol Returns true if the strings are equal or the first non-matching character in left has a codepoint greater than the character in right (or if left string is longer)

Binary less-than-or-equals

<left-value> <= <right-value>

Implements <left-value>.<=(<right-value>)

Standard less-than-or-equals operator, like inverse of greater-than.

Class Action
Fixnum, Bignum, Float, Rational Returns true if left less than or equal to right, otherwise false
Time Returns true if left less than or equal to right, otherwise false
Module Returns true if left is the same or a subclass of right, false if it's a superclass, or nil if it's not related
String, Symbol Returns true if the strings are equal or the first non-matching character in left has a codepoint less than the character in right (or if left string is shorter)

Binary comparison

<left-value> <=> <right-value>

Implements <left-value>.<=>(<right-value>)

The comparison operator combines less than, greater than and equals tests into one operator. It is expected to return -1 if less than, 0 if equal, 1 if greater than and nil if uncomparable.

Class Action
Fixnum, Bignum, Float, Rational Returns a value in accordance with >, <, >=, <= operators
Object Returns 0 if equal, otherwise nil
Numeric Returns 0 if both operands the same object, otherwise nil
Time Returns a value in accordance with >, <, >=, <= operators
Module Returns a value in accordance with >, <, >=, <= operators
String, Symbol Returns a value in accordance with >, <, >=, <= operators
Array Compares left and right arrays' elements pairwise (with <=> until not 0, and returns the value, otherwise returns 1 if left array longer, -1 if left array shorter, or 0 if arrays the same

Binary equals

<left-value> == <right-value>

Implements <left-value>.==(<right-value>)

Used to test if 2 values have equivalent meaning.

Class Action
BasicObject, Module Returns true if both values are the same object, otherwise false
Fixnum, Bignum, Float, Rational, Complex Returns true if both values' numeric meaning are the same, otherwise false
Symbol Returns true if both values are the same symbol, otherwise false
String Returns true if both values are the same string, otherwise false
Time Returns true if both values have the same time, otherwise false
Array Returns true if both arrays are the same length and each corresponding element is equal (by ==)
Hash Returns true if both hashes are the same size and the value corresponding to each key is equal in both hashes (by ==)
Range Returns true if both ranges have equal beginning and end values (by ==) and they both have the same "exclude end value" flag
Regexp Returns true if both Regexp objects have the same properties, otherwise false
Match Returns true if both Match objects have the same properties, otherwise false
Proc Returns true if both Proc objects have the same instructions in their block, and the same closure
Method, UnboundMethod Returns true if both Method objects are the same method (or an alias) and are bound to the same object
Exception Returns true if both Exceptions have the same class, message and backtrace

Binary not-equals

<left-value> != <right-value>

Implements <left-value>.!=(<right-value>)

Inverse of ==

Class Action
BasicObject, Module Returns false if both values ==, otherwise true

Binary case equals

<left-value> === <right-value>

Implements <left-value>.===(<right-value>)

Used to match in a case when or rescue clause, designed to match a value (on the right) to a category of values (on the left). Classes can be matched to their superclasses, values can be matched to a Range or Regexp pattern, and values can be matched to the result of calling a Proc with the value as an argument.

Class Action
Kernel Returns true if the values are the same object or if the values ==
Fixnum, Bignum, Float, String, Symbol Same implementation as ==
Module Returns true if left value is the same as or a superclass of the right value, otherwise false
Proc Same implementation as call
Range Returns <left-value>.include?(<right-value>)
Regexp Returns true if regexp matches to right value, otherwise false

Array referencing syntax

Syntactically speaking, Array references are not really operators, e.g. they don't really have operands or precedence in the usual sense. However, they are syntax elements which are class-defined, so we will describe their standard implementations here.

Array reference

<obj>[<args>]

Implements <obj>.[](<args>)
Class Action
Array If a single integer provided, returns element at that index. If two integers (start and length) or a Range of integers are provided, returns an array containing the elements in that range. Negative indices are references from the end of the array
Hash Returns the value corresponding to the given key argument
Symbol, String Similar to Array if you consider the left value as an array of single-character strings, also you can reference with a Regexp argument (returns matched string), a Regexp and integer/name index (returns matched subexpression), or a String (returns string argument if a substring matches it)
Fixnum, Bignum Returns the bit at the given integer index (beginning from least significant bit)
MatchData Similar to Array if you consider it an array of strings matched beginning with the whole pattern and followed by subexpression matches. Also can index by a string to get a named subexpression
Thread Access a value in the thread-local storage hash by string or (equivalently) symbol
Proc, Method, Continuation Same implementation as call

Array set

<obj>[<args>] = <value>

Implements <obj>.[]=(<args>, <value>)
Class Action
Array Like inverse of array reference
Hash Like inverse of array reference
String Like inverse of array reference
Thread Like inverse of array reference

Language-defined operators

The following operators are defined by the language - their behaviour can't be altered.

Simple assignment

<lhs> = <value>
<lhs> = <asgn_value> rescue <resc_value>

Returns: The value assigned

Same as other languages - assigns a value to a variable. <lhs> can be:

  • a local variable
  • an instance variable
  • a class variable
  • a global variable
  • an array reference
  • an attribute reference
  • a constant reference

Note that a local variable in <lhs> which did not previously exist will be created by this expression. That same local variable can actually be used as part of the <value> expression - its value in such usage will be nil.

In some circumstances a rescue modifier may be attached to the assignment expression, whereas normally they can only be applied to whole statements. This results in the value of <asgn_value> rescue <resc_value> being assigned to the variable. See the operator precedence section for rules on when this is permitted.

Multiple right-hand-side assignment

<lhs> = <mrhs>

Returns: the value of <mrhs> as encapsulated in an Array

<mrhs> is a comma-separated list of regular and splat call arguments, and is processed similar to a method call. The resulting list of values is put in an Array, and this value is assigned to the <lhs>. In other words, this syntax is equivalent to assigning an array literal with <lhs> = [ <mrhs> ] except that the <mrhs> syntax does not permit hash associations or trailing commas.

Multiple left-hand-side assignment

<mlhs> = <value>
<mlhs> = <mrhs>

Returns: the value of <value> or <mrhs>

Multiple comma-separated variable references are provided in <mlhs>, and the value to be assigned is treated as an Array, its contents assigned to variable references in <mlhs>. Any variable references permitted in a simple assignments are also permitted in multiple left-hand-side assignments.

<mlhs> is a comma-separated list of at least 1 of the following elements, in order:

  • 0 or more nested "mandatory" variable references, which may be (in any order):
  • Simple variable references
  • Nested multiple left-hand-side structures enclosed in ( and )
  • An optional "rest" variable reference, which may be:
  • A regular "rest" variable reference, which is written as a variable reference prefixed by * (e.g. *rest)
  • An anonymous "rest" reference, which is written as *
  • If a "rest" variable reference is present
  • 0 or more "post" variable references, which have the same form as nested "mandatory" variable references

Nested multiple left-hand-side structures have the same form as <mlhs>.

Some additional syntax rules apply:

  • If <mlhs> or a nested multiple left-hand-side structure contains only "mandatory" references, it may be followed by a trailing ,
  • If there is only a single "mandatory" reference, it MUST be followed by a trailing ,
  • <mlhs> (excluding nested structures) may optionally be enclosed in ( and ), without changing the meaning of the expression (i.e. without it being considered a nested expression). Note that this means that a, b = c and (a, b) = c mean the same thing, however ((a, b)) = c is different.

A value is assigned to <mlhs> as follows:

  • Convert the value to Array if necessary:
  • If the value is already an Array or an instance of a subclass of Array, OK
  • Otherwise, conversion to an Array is attempted by calling the to_ary method on the value
    • If the to_ary method call fails to resolve or returns nil, the conversion has failed
    • If the returned object from to_ary is not a subclass of Array, an exception is thrown
    • Otherwise, OK
  • If the conversion failed (without throwing an exception), then the conversion is effected by creating a new Array of 1 element, which is the value
  • The contents of the array are assigned to:
  • "mandatory" variable references, from left to right, then
  • if a "rest" reference is present, any values in the array in excess of the number of mandatory/post references are selected and:
    • if the "rest" reference is anonymous, ignored
    • otherwise, compiled into an array and assigned to the "rest" reference
  • "post" variable references, from left to right
  • if there are fewer elements in the array than there are mandatory/post variable references, the unset references are set to nil
  • Any remaining values in the array are ignored
  • If there is a regular "rest" variable reference and no array elements to assign to it, it is assigned an empty array
  • If there are nested multiple left-hand-side structures in mandatory or post, this array conversion and assignment process is repeated for them

Some examples:

a, b = [1, 2, 3] # a = 1, b = 2
a, *, (b, c) = [1, 2, 3, [4, 5]] # a = 1, b = 4, c = 5

Complex assignment - binary arithmetic operators

<lhs> <op>= <value>

Returns: the value of after the expression is processed

There are complex assignment forms for all the binary arithmetic operators, formed by writing an = on the end of the operator e.g. +=. For these operators, an expression a <op>= b is equivalent to a = a <op> b, e.g. a += b is equivalent to a = a + b.

All complex assignments may not have multiple left- or right-hand-sides, and they may not have explicitly-scoped constant references on their left-hand-side.

Complex assignment - logical operators

<lhs> &&= <value>
<lhs> ||= <value>

Returns: the value of after the expression is processed

Mostly, these are similar to other complex assignment operators (i.e. a &&= b is like a = a && b), except:

  • If the expression is short circuited and <value> is never evaluated, then the assignment is not performed at all, which has implications for assignments that may have side effects, especially:
  • global variable assignments may cause trace procedures to be invoked
  • constant assignments may assign to a different constant than the one that is accessed
  • attribute and array reference assignments invoke methods to perform the assignment, which may have whatever side effects they like
  • In some cases, the ||= expression has the ability to determine that <lhs> is falsy when it's not defined at all - so it assigns to the <lhs> when other complex assignments would throw an exception

Specific behaviour for a &&= b:

  • If a is other than a local variable, equivalent to a && (a = b), i.e. doesn't assign if a is falsy
  • If a is a local variable, it is equivalent to a && (a = b) except that if a was not defined before the expression, a &&= b defines it and therefore doesn't cause a syntax error (whereas a && (a = b) would cause a syntax error). Since assigning to local variables has no side effects, you might say this form is equivalent to a = a && b even though an assignment is not performed if a is falsy

Specific behaviour for a ||= b:

  • If a is an array or attribute reference, equivalent to a || (a = b), i.e. doesn't assign if a is truthy
  • If a is a local variable, it is equivalent to a || (a = b) except that if a was not defined before the expression, a ||= b defines it and therefore doesn't cause a syntax error (whereas a || (a = b) would cause a syntax error). Since assigning to local variables has no side effects, you might say this form is equivalent to a = a || b even though an assignment is not performed if a is truthy
  • If a is a global variable, instance variable, class variable, or (implicitly namespaced) constant, it will also test to see if the referenced variable is defined and, if not, perform the assignment i.e. it's equivalent to (defined? a && a) || (a = b). For undefined global variables, this appears to make no difference as they return nil anyway, but for the others it prevents an exception being thrown due to the variables being undefined

Logical operators

Logical and

<value> && <value>
<value> and <value>

Returns: the left value, if falsy, otherwise the right value

Similar to other languages, in that it returns a truthy value if both operands are truthy, and a falsy value otherwise. Uses short-circuit evaluation - the second operand is not evaluated if the first is falsy. Typically used to test whether two values are both truthy, however can also be used to evaluate the second value on the condition that the first one is truthy.

a && b therefore behaves like:

(
  tmp = a
  if tmp
    b
  else
    tmp
  end
)

Both &&, and forms of the operator have the same meaning, however they have different positions in the syntax grammar and precedence.

Logical or

<value> || <value>
<value> or <value>

Returns: the left value, if truthy, otherwise the right value

Similar to other languages, in that returns a truthy value if one of the operands is truthy, and a falsy value otherwise. Uses short-circuit evaluation - the second operand is not evaluated if the first is truthy. Typically used to test whether at least one of two values is truthy, however can also be used to evaluate the second value on the condition that the first one is falsy.

a || b therefore behaves like:

(
  tmp = a
  unless tmp
    b
  else
    tmp
  end
)

Both ||, or forms of the operator have the same meaning, however they have different positions in the syntax grammar and precedence.

Ternary ?:

<cond> ? <value-if-true> : <value-if-false>

Returns: see if expression

Standard ternary operator - equivalent to if <cond> then <value-if-true> else <value-if-false> end, including the special condition processing that applies to if expressions

defined?

defined? <expr>

Returns: nil if undefined, otherwise a String (see below)

The defined? operator is used to test whether <expr> is defined. <expr> is not evaluated, its structure is examined at runtime to determine whether it is defined. If not, it returns nil, otherwise it returns a String. The behaviour of defined? based on the <expr> that is passed to it is described below:

<expr> Is defined if Return value
nil Always "nil"
self Always "self"
true Always "true"
false Always "false"
An array literal If contains other than simple (non splat/hash) elements, or every element within is defined? (short-circuiting on the first failure) "expression"
A local variable Always (a local variable which is not defined is a method call) "local-variable"
An instance variable The variable is found "instance-variable"
A class variable The variable is found "class-variable"
$&, $` , $', $", backreferences The variable is not nil "global-variable"
An other global variable The variable exists (except for where it has been referenced but never assigned to) "global-variable"
An implicitly-namespaced constant The constant is defined according to constant-defined logic "constant"
An explicitly-namespaced constant The namespace is defined? and (after evaluating it) the constant is defined on it according to constant-defined logic "constant"
Any type of method call with a block Always "expression"
An implicitly-targeted method call If the method is defined for self, it isn't flagged "not implemented", and if all its arguments are mandatory, they are all defined (short-circuiting on the first failure) "method"
An array reference on self As above for the array reference method "method"
A simple assignment to an array/attribute reference on self As above for the array/attribute set method "method"
An explicitly-targeted method call If the target is defined and, after evaluating it, the method is defined for it and its visibility makes it accessible from the current scope, or respond_to_missing?(:method, false) on the target indicates it responds, and if all its arguments are mandatory, they are all defined (short-circuiting on the first failure) "method"
An array reference on other than self As above for the array reference method "method"
A simple assignment to an array/attribute reference on other than self As above for the array/attribute set method "method"
A class-defined operator expression As above for the operator method "method"
A yield expression If there is a block passed to the current method "yield"
A super expression If the super call would resolve according to normal logic, except no method_missing, "not implemented" methods are counted undefined, and buggy interactions with define_method "super"
Any other assignment expression Always "assignment"
Any other expression Always "expression"

Note that in some circumstances defined? evaluates part of its expression. If this evaluation raises an exception, defined? will catch this exception and return nil.

Double-dot and triple-dot

<from-value> .. <to-value>
<from-value> ... <to-value>

These operators serve 2 different functions.

Range constructors

Returns: a Range object

These operators are normally Range constructors, and are equivalent to the default implementations of:

  • For .., Range.new(<from-value>, <to-value>, false)
  • For ..., Range.new(<from-value>, <to-value>, true)

Flip-flops

Returns: the flip-flop's state

If these operators appear (in some circumstances) within the conditional expression of if, elsif, unless, while, until, ?:, not and !, they are flip-flops, not range constructors.

Flip-flops create a special hidden variable in the local variable context, which is used to keep track of whether the flip-flop is "on" or "off" - it is this state which is returned as the value of the flip-flop. A flip-flop starts its life "off", and will be "on" from the point when the <from-value> expression is truthy up to and including the point where the <to-value> is truthy. The difference between the two forms is that for ..., the <to-value> expression will not be evaluated when the <from-value> first becomes truthy i.e. if both values become truthy at the same time, this will not cause the flip-flop to be switched "off" after once being reported "on".

If either value is an Fixnum integer literal, it is equivalent to comparing via == to $. i.e. 3 is equivalent to 3 == $..

A flip-flop is not a Ruby value and can't be manipulated as such - they are purely a feature of conditional expressions.

Here is some Ruby code describing the behaviour of flip-flops, assuming tmp is a local context variable.

changed = false

unless tmp # flip-flop is off
  if from_value
    tmp = true
    changed = true
  end
end

if tmp && !(changed && operator == "...") # flip-flop is on, but don't try to switch off if it was just switched on and operator is "..."
  if to_value
    tmp = false
    changed = true
  end
end

(!tmp && changed) ? true : tmp # report "on" up to and including the moment it's switched "off"

Operator precedence

Broadly, the following table describes the precedence of Ruby operators.

No|Type/Associativity|Description|Operators ---|---|---|---|--- 1|Binary left|Reference|. :: [] 2|Unary|General|! ~ + 3|Binary right|Power|** 4|Unary|Minus|- 5|Binary left|Products|* / % 6|Binary left|Additions|+ - 7|Binary left|Shifts|<< >> 8|Binary left|Bitwise and|& 9|Binary left|Bitwise or|| ^ 10|Binary left (TODO why?)|Directional comparisons|> >= < <= 11|Binary none|General comparisons|<=> == === != =~ !~ 12|Binary left|Logical and, argument form|&& 13|Binary left|Logical or, argument form||| 14|Binary left|Range constructors|.. ... 15|Ternary right|Logical ternary|?: 16|Unary|Defined|defined? 17|Binary none|Assignment modifier|rescue 18|Binary right|Assignments|= += -= etc. 19|Unary|Logical not, expression form|not 20|Binary left|Logical and/or, expression form|and or 21|Binary left|Statement modifiers|if unless while until rescue

Some special rules also apply:

  • If a rescue modifier is applied to an assignment, e.g. <lhs> = <rhs> rescue <value>, rescue becomes an assignment modifier and takes precedence over the assignment operator (i.e. it will be interpreted as <lhs> = ( <rhs> rescue <value> )) according to the following rules:
  • The assignment operator may be simple or complex
  • This does not apply to an assignment with a multiple left-hand-side, a multiple right-hand-side or a command call on the right-hand-side
  • If the argument to defined? or not? is within parentheses and there is no whitespace preceding it, then the operator has the highest precedence (higher even than . and ::)

Clone this wiki locally