Expressions¶
Chapel provides the following expressions:
expression:
literal-expression
variable-expression
enum-constant-expression
call-expression
type-expression
iteratable-call-expression
member-access-expression
new-expression
query-expression
cast-expression
lvalue-expression
parenthesized-expression
unary-expression
binary-expression
let-expression
if-expression
for-expression
forall-expression
reduce-expression
scan-expression
module-access-expression
tuple-expression
tuple-expand-expression
locale-query-expression
type-query-expression
mapped-domain-expression
Individual expressions are defined in the remainder of this chapter and additionally as follows:
forall, reduce, and scan Data Parallelism
module access Qualified Naming of Module Symbols
tuple and tuple expand Tuples
locale query with
.locale
Querying the Locale of an Expressiontype query with
.type
Querying the Type of an Expressionmapped domain Domain Maps
initializer calls Class New
Literal Expressions¶
A literal value for any of the predefined types is a literal expression.
Literal expressions are given by the following syntax:
literal-expression:
bool-literal
integer-literal
real-literal
imaginary-literal
string-literal
bytes-literal
range-literal
domain-literal
array-literal
Literal values for primitive types are described in Literals. Literal range values are described in Range Literals. Literal tuple values are described in Tuple Values. Literal values for domains are described in Rectangular Domain Values and Associative Domain Values. Literal values for arrays are described in Rectangular Array Literals and Associative Array Literals.
Variable Expressions¶
A use of a variable, constant, parameter, or formal argument, is itself an expression. The syntax of a variable expression is given by:
variable-expression:
identifier
Enumeration Constant Expression¶
A use of an enumeration constant is itself an expression. Such a constant must be preceded by the enumeration type name. The syntax of an enumeration constant expression is given by:
enum-constant-expression:
enum-type . identifier
For an example of using enumeration constants, see Enumerated Types.
Parenthesized Expressions¶
A parenthesized-expression
is an expression that is delimited by
parentheses as given by:
parenthesized-expression:
( expression )
Such an expression evaluates to the expression. The parentheses are ignored and have only a syntactical effect.
Call Expressions¶
Functions and function calls are defined in Procedures.
Indexing Expressions¶
Indexing, for example into arrays, tuples, and domains, has the same syntax as a call expression.
Indexing is performed by an implicit invocation of the this
method
on the value being indexed, passing the indices as the actual arguments.
Member Access Expressions¶
Member access expressions provide access to a field or invoke a method of an instance of a class, record, or union. They are defined in Field Accesses and Class Method Calls, respectively.
member-access-expression:
field-access-expression
method-call-expression
The Query Expression¶
A query expression is used to query a type or value within a formal argument type expression. The syntax of a query expression is given by:
query-expression:
? identifier[OPT]
Querying is restricted to querying the type of a formal argument, the element type of a formal argument that is an array, the domain of a formal argument that is an array, the size of a primitive type, or a type or parameter field of a formal argument type.
The identifier can be omitted. This is useful for ensuring the genericity of a generic type that defines default values for all of its generic fields when specifying a formal argument as discussed in Formal Arguments of Generic Type.
Example (query.chpl).
The following code defines a generic function where the type of the first argument is queried and stored in the type alias
t
and the domain of the second argument is queried and stored in the variableD
:proc foo(x: ?t, y: [?D] t) { for i in D do y[i] = x; }This allows a generic specification of assigning a particular value to all elements of an array. The value and the elements of the array are constrained to be the same type. This function can be rewritten without query expression as follows:
proc foo(x, y: [] x.type) { for i in y.domain do y[i] = x; }
There is an expectation that query expressions will be allowed in more places in the future.
Casts¶
A cast is specified with the following syntax:
cast-expression:
expression : type-expression
The expression is converted to the specified type. A cast expression invokes the corresponding explicit conversion (Explicit Conversions). A resolution error occurs if no such conversion exists.
LValue Expressions¶
An lvalue is an expression that can be used on the left-hand side of
an assignment statement or on either side of a swap statement, that can
be passed to a formal argument of a function that has out
, inout
or ref
intent, or that can be returned by a function with a ref
return intent (The Ref Return Intent). Valid lvalue
expressions include the following:
Variable expressions.
Member access expressions.
Call expressions of functions with a
ref
return intent.Indexing expressions.
LValue expressions are given by the following syntax:
lvalue-expression:
variable-expression
member-access-expression
call-expression
parenthesized-expression
The syntax is less restrictive than the definition above. For example,
not all call-expression
s are lvalues.
Precedence and Associativity¶
Operator |
Associativity |
Use |
---|---|---|
. () [] |
left |
member access
function call or access
function call or access
|
|
right |
initializer call |
owned shared borrowed unmanaged |
right |
apply management strategy to a class |
postfix
? postfix
! |
left |
compute a nilable class type
assert non-nilable and borrow
|
|
left |
cast |
|
right |
exponentiation |
reduce scan dmapped |
left scan |
reduction
scan
domain map application
|
prefix
! ~ |
right |
logical negation
bitwise negation
|
* / % |
left |
multiplication
division
modulus
|
unary
+ unary
- |
right |
positive identity
negation
|
<< >> |
left |
left shift
right shift
|
|
left |
bitwise/logical and |
|
left |
bitwise/logical xor |
|
left |
bitwise/logical or |
+ - |
left |
addition
subtraction
|
.. ..< |
left
left
|
range initialization
open-interval range initialization
|
<= >= < > |
left |
less-than-or-equal-to comparison
greater-than-or-equal-to comparison
less-than comparison
greater-than comparison
|
|
left |
equal-to comparison not-equal-to comparison |
|
left |
short-circuiting logical and |
|
left |
short-circuiting logical or |
by # align |
left |
range/domain stride application
range count application
range alignment
|
|
left |
forall expression |
if then else forall do [ ] for do sync single atomic |
left |
conditional expression
forall expression
forall expression
for expression
sync type modifier
single type modifier
atomic type modifier
|
|
left |
comma separated expressions |
The above table summarizes operator and expression precedence and associativity. Operators and expressions listed earlier have higher precedence than those listed later.
Rationale.
In general, our operator precedence is based on that of the C family of languages including C++, Java, Perl, and C#. We comment on a few of the differences and unique factors here.
We find that there is tension between the relative precedence of exponentiation, unary minus/plus, and casts. The following three expressions show our intuition for how these expressions should be parenthesized.
-2**4
wants
-(2**4)
-2:uint
wants
(-2):uint
2:uint**4:uint
wants
(2:uint)**(4:uint)
Trying to support all three of these cases results in a circularity—exponentiation wants precedence over unary minus, unary minus wants precedence over casts, and casts want precedence over exponentiation. We chose to break the circularity by making unary minus have a lower precedence. This means that for the second case above:
-2:uint
requires
(-2):uint
We also chose to depart from the C family of languages by making unary plus/minus have lower precedence than binary multiplication, division, and modulus as in Fortran. We have found very few cases that distinguish between these cases. An interesting one is:
- ::
const minint = min(int(32));`` …-minint/2…``
Intuitively, this should result in a positive value, yet C’s precedence rules results in a negative value due to asymmetry in modern integer representations. If we learn of cases that argue in favor of the C approach, we would likely reverse this decision in order to more closely match C.
We were tempted to diverge from the C precedence rules for the binary bitwise operators to make them bind less tightly than comparisons. This would allow us to interpret:
a | b == 0
as
(a | b) == 0
However, given that no other popular modern language has made this change, we felt it unwise to stray from the pack. The typical rationale for the C ordering is to allow these operators to be used as non-short-circuiting logical operations.
In contrast to C, we give bitwise operations a higher precedence than binary addition/subtraction and comparison operators. This enables using the shift operators as shorthand for multiplication/division by powers of 2, and also makes it easier to extract and test a bitmapped field:
(x & MASK) == MASK
as
x & MASK == MASK
a + b * pow(2,y)
as
a * b << y
One final area of note is the precedence of reductions. Two common cases tend to argue for making reductions very low or very high in the precedence table:
max reduce A - min reduce A
wants
(max reduce A) - (min reduce A)
max reduce A * B
wants
max reduce (A * B)
The first statement would require reductions to have a higher precedence than the arithmetic operators while the second would require them to be lower. We opted to make reductions have high precedence due to the argument that they tend to resemble unary operators. Thus, to support our intuition:
max reduce A * B
requires
max reduce (A * B)
This choice also has the (arguably positive) effect of making the unparenthesized version of this statement result in an aggregate value if A and B are both aggregates—the reduction of A results in a scalar which promotes when being multiplied by B, resulting in an aggregate. Our intuition is that users who forget the parentheses will learn of their error at compilation time because the resulting expression is not a scalar as expected.
Operator Expressions¶
The application of operators to expressions is itself an expression. The syntax of a unary expression is given by:
unary-expression:
unary-operator expression
unary-operator: one of
+ - ~ !
The syntax of a binary expression is given by:
binary-expression:
expression binary-operator expression
binary-operator: one of
+ - * / % ** & | ^ << >> && || == != <= >= < > 'by' #
The operators are defined in subsequent sections.
Arithmetic Operators¶
This section describes the predefined arithmetic operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
For each operator, implicit conversions are applied to the operands of an operator such that they are compatible with one of the function forms listed, those listed earlier in the list being given preference. If no compatible implicit conversions exist, then a compile-time error occurs. In these cases, an explicit cast is required.
Unary Plus Operators¶
The unary plus operators are predefined as follows:
operator +(a: int(8)): int(8)
operator +(a: int(16)): int(16)
operator +(a: int(32)): int(32)
operator +(a: int(64)): int(64)
operator +(a: uint(8)): uint(8)
operator +(a: uint(16)): uint(16)
operator +(a: uint(32)): uint(32)
operator +(a: uint(64)): uint(64)
operator +(a: real(32)): real(32)
operator +(a: real(64)): real(64)
operator +(a: imag(32)): imag(32)
operator +(a: imag(64)): imag(64)
operator +(a: complex(64)): complex(64)
operator +(a: complex(128)): complex(128)
For each of these definitions, the result is the value of the operand.
Unary Minus Operators¶
The unary minus operators are predefined as follows:
operator -(a: int(8)): int(8)
operator -(a: int(16)): int(16)
operator -(a: int(32)): int(32)
operator -(a: int(64)): int(64)
operator -(a: real(32)): real(32)
operator -(a: real(64)): real(64)
operator -(a: imag(32)): imag(32)
operator -(a: imag(64)): imag(64)
operator -(a: complex(64)): complex(64)
operator -(a: complex(128)): complex(128)
For each of these definitions that return a value, the result is the negation of the value of the operand. For integral types, this corresponds to subtracting the value from zero. For real and imaginary types, this corresponds to inverting the sign. For complex types, this corresponds to inverting the signs of both the real and imaginary parts.
It is an error to try to negate a value of type uint(64)
. Note that
negating a value of type uint(32)
first converts the type to
int(64)
using an implicit conversion.
Addition Operators¶
The addition operators are predefined as follows:
operator +(a: int(8), b: int(8)): int(8)
operator +(a: int(16), b: int(16)): int(16)
operator +(a: int(32), b: int(32)): int(32)
operator +(a: int(64), b: int(64)): int(64)
operator +(a: uint(8), b: uint(8)): uint(8)
operator +(a: uint(16), b: uint(16)): uint(16)
operator +(a: uint(32), b: uint(32)): uint(32)
operator +(a: uint(64), b: uint(64)): uint(64)
operator +(a: real(32), b: real(32)): real(32)
operator +(a: real(64), b: real(64)): real(64)
operator +(a: imag(32), b: imag(32)): imag(32)
operator +(a: imag(64), b: imag(64)): imag(64)
operator +(a: complex(64), b: complex(64)): complex(64)
operator +(a: complex(128), b: complex(128)): complex(128)
operator +(a: real(32), b: imag(32)): complex(64)
operator +(a: imag(32), b: real(32)): complex(64)
operator +(a: real(64), b: imag(64)): complex(128)
operator +(a: imag(64), b: real(64)): complex(128)
operator +(a: real(32), b: complex(64)): complex(64)
operator +(a: complex(64), b: real(32)): complex(64)
operator +(a: real(64), b: complex(128)): complex(128)
operator +(a: complex(128), b: real(64)): complex(128)
operator +(a: imag(32), b: complex(64)): complex(64)
operator +(a: complex(64), b: imag(32)): complex(64)
operator +(a: imag(64), b: complex(128)): complex(128)
operator +(a: complex(128), b: imag(64)): complex(128)
For each of these definitions that return a value, the result is the sum of the two operands.
It is a compile-time error to add a value of type uint(64)
and a
value of type int(64)
.
Addition over a value of real type and a value of imaginary type produces a value of complex type. Addition of values of complex type and either real or imaginary types also produces a value of complex type.
Subtraction Operators¶
The subtraction operators are predefined as follows:
operator -(a: int(8), b: int(8)): int(8)
operator -(a: int(16), b: int(16)): int(16)
operator -(a: int(32), b: int(32)): int(32)
operator -(a: int(64), b: int(64)): int(64)
operator -(a: uint(8), b: uint(8)): uint(8)
operator -(a: uint(16), b: uint(16)): uint(16)
operator -(a: uint(32), b: uint(32)): uint(32)
operator -(a: uint(64), b: uint(64)): uint(64)
operator -(a: real(32), b: real(32)): real(32)
operator -(a: real(64), b: real(64)): real(64)
operator -(a: imag(32), b: imag(32)): imag(32)
operator -(a: imag(64), b: imag(64)): imag(64)
operator -(a: complex(64), b: complex(64)): complex(64)
operator -(a: complex(128), b: complex(128)): complex(128)
operator -(a: real(32), b: imag(32)): complex(64)
operator -(a: imag(32), b: real(32)): complex(64)
operator -(a: real(64), b: imag(64)): complex(128)
operator -(a: imag(64), b: real(64)): complex(128)
operator -(a: real(32), b: complex(64)): complex(64)
operator -(a: complex(64), b: real(32)): complex(64)
operator -(a: real(64), b: complex(128)): complex(128)
operator -(a: complex(128), b: real(64)): complex(128)
operator -(a: imag(32), b: complex(64)): complex(64)
operator -(a: complex(64), b: imag(32)): complex(64)
operator -(a: imag(64), b: complex(128)): complex(128)
operator -(a: complex(128), b: imag(64)): complex(128)
For each of these definitions that return a value, the result is the value obtained by subtracting the second operand from the first operand.
It is a compile-time error to subtract a value of type uint(64)
from
a value of type int(64)
, and vice versa.
Subtraction of a value of real type from a value of imaginary type, and vice versa, produces a value of complex type. Subtraction of values of complex type from either real or imaginary types, and vice versa, also produces a value of complex type.
Multiplication Operators¶
The multiplication operators are predefined as follows:
operator *(a: int(8), b: int(8)): int(8)
operator *(a: int(16), b: int(16)): int(16)
operator *(a: int(32), b: int(32)): int(32)
operator *(a: int(64), b: int(64)): int(64)
operator *(a: uint(8), b: uint(8)): uint(8)
operator *(a: uint(16), b: uint(16)): uint(16)
operator *(a: uint(32), b: uint(32)): uint(32)
operator *(a: uint(64), b: uint(64)): uint(64)
operator *(a: real(32), b: real(32)): real(32)
operator *(a: real(64), b: real(64)): real(64)
operator *(a: imag(32), b: imag(32)): real(32)
operator *(a: imag(64), b: imag(64)): real(64)
operator *(a: complex(64), b: complex(64)): complex(64)
operator *(a: complex(128), b: complex(128)): complex(128)
operator *(a: real(32), b: imag(32)): imag(32)
operator *(a: imag(32), b: real(32)): imag(32)
operator *(a: real(64), b: imag(64)): imag(64)
operator *(a: imag(64), b: real(64)): imag(64)
operator *(a: real(32), b: complex(64)): complex(64)
operator *(a: complex(64), b: real(32)): complex(64)
operator *(a: real(64), b: complex(128)): complex(128)
operator *(a: complex(128), b: real(64)): complex(128)
operator *(a: imag(32), b: complex(64)): complex(64)
operator *(a: complex(64), b: imag(32)): complex(64)
operator *(a: imag(64), b: complex(128)): complex(128)
operator *(a: complex(128), b: imag(64)): complex(128)
For each of these definitions that return a value, the result is the product of the two operands.
It is a compile-time error to multiply a value of type uint(64)
and
a value of type int(64)
.
Multiplication of values of imaginary type produces a value of real type. Multiplication over a value of real type and a value of imaginary type produces a value of imaginary type. Multiplication of values of complex type and either real or imaginary types produces a value of complex type.
Division Operators¶
The division operators are predefined as follows:
operator /(a: int(8), b: int(8)): int(8)
operator /(a: int(16), b: int(16)): int(16)
operator /(a: int(32), b: int(32)): int(32)
operator /(a: int(64), b: int(64)): int(64)
operator /(a: uint(8), b: uint(8)): uint(8)
operator /(a: uint(16), b: uint(16)): uint(16)
operator /(a: uint(32), b: uint(32)): uint(32)
operator /(a: uint(64), b: uint(64)): uint(64)
operator /(a: real(32), b: real(32)): real(32)
operator /(a: real(64), b: real(64)): real(64)
operator /(a: imag(32), b: imag(32)): real(32)
operator /(a: imag(64), b: imag(64)): real(64)
operator /(a: complex(64), b: complex(64)): complex(64)
operator /(a: complex(128), b: complex(128)): complex(128)
operator /(a: real(32), b: imag(32)): imag(32)
operator /(a: imag(32), b: real(32)): imag(32)
operator /(a: real(64), b: imag(64)): imag(64)
operator /(a: imag(64), b: real(64)): imag(64)
operator /(a: real(32), b: complex(64)): complex(64)
operator /(a: complex(64), b: real(32)): complex(64)
operator /(a: real(64), b: complex(128)): complex(128)
operator /(a: complex(128), b: real(64)): complex(128)
operator /(a: imag(32), b: complex(64)): complex(64)
operator /(a: complex(64), b: imag(32)): complex(64)
operator /(a: imag(64), b: complex(128)): complex(128)
operator /(a: complex(128), b: imag(64)): complex(128)
For each of these definitions that return a value, the result is the quotient of the two operands.
It is a compile-time error to divide a value of type uint(64)
by a
value of type int(64)
, and vice versa.
Division of values of imaginary type produces a value of real type. Division over a value of real type and a value of imaginary type produces a value of imaginary type. Division of values of complex type and either real or imaginary types produces a value of complex type.
When the operands are integers, the result (quotient) is also an
integer. If b
does not divide a
exactly, then there are two
candidate quotients \(q1\) and \(q2\) such that \(b * q1\)
and \(b * q2\) are the two multiples of b
closest to a
. The
integer result \(q\) is the candidate quotient which lies closest to
zero.
Modulus Operators¶
The modulus operators are predefined as follows:
operator %(a: int(8), b: int(8)): int(8)
operator %(a: int(16), b: int(16)): int(16)
operator %(a: int(32), b: int(32)): int(32)
operator %(a: int(64), b: int(64)): int(64)
operator %(a: uint(8), b: uint(8)): uint(8)
operator %(a: uint(16), b: uint(16)): uint(16)
operator %(a: uint(32), b: uint(32)): uint(32)
operator %(a: uint(64), b: uint(64)): uint(64)
For each of these definitions that return a value, the result is the remainder when the first operand is divided by the second operand.
The sign of the result is the same as the sign of the dividend a
,
and the magnitude of the result is always smaller than that of the
divisor b
. For integer operands, the %
and /
operators are
related by the following identity:
var q = a / b;
var r = a % b;
writeln(q * b + r == a); // true
It is a compile-time error to take the remainder of a value of type
uint(64)
and a value of type int(64)
, and vice versa.
There is an expectation that the predefined modulus operators will be extended to handle real, imaginary, and complex types in the future.
Exponentiation Operators¶
The exponentiation operators are predefined as follows:
operator **(a: int(8), b: int(8)): int(8)
operator **(a: int(16), b: int(16)): int(16)
operator **(a: int(32), b: int(32)): int(32)
operator **(a: int(64), b: int(64)): int(64)
operator **(a: uint(8), b: uint(8)): uint(8)
operator **(a: uint(16), b: uint(16)): uint(16)
operator **(a: uint(32), b: uint(32)): uint(32)
operator **(a: uint(64), b: uint(64)): uint(64)
operator **(a: real(32), b: real(32)): real(32)
operator **(a: real(64), b: real(64)): real(64)
For each of these definitions that return a value, the result is the value of the first operand raised to the power of the second operand.
It is a compile-time error to take the exponent of a value of type
uint(64)
by a value of type int(64)
, and vice versa.
There is an expectation that the predefined exponentiation operators will be extended to handle imaginary and complex types in the future.
Bitwise Operators¶
This section describes the predefined bitwise operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
Bitwise Complement Operators¶
The bitwise complement operators are predefined as follows:
operator ~(a: int(8)): int(8)
operator ~(a: int(16)): int(16)
operator ~(a: int(32)): int(32)
operator ~(a: int(64)): int(64)
operator ~(a: uint(8)): uint(8)
operator ~(a: uint(16)): uint(16)
operator ~(a: uint(32)): uint(32)
operator ~(a: uint(64)): uint(64)
For each of these definitions, the result is the bitwise complement of the operand.
Bitwise And Operators¶
The bitwise and operators are predefined as follows:
operator &(a: bool, b: bool): bool
operator &(a: int(?w), b: int(w)): int(w)
operator &(a: uint(?w), b: uint(w)): uint(w)
operator &(a: int(?w), b: uint(w)): uint(w)
operator &(a: uint(?w), b: int(w)): uint(w)
For each of these definitions, the result is computed by applying the logical and operation to the bits of the operands.
Chapel allows mixing signed and unsigned integers of the same size when passing them as arguments to bitwise and. In the mixed case the result is of the same size as the arguments and is unsigned. No run-time error is issued, even if the apparent sign changes as the required conversions are performed.
Rationale.
The mathematical meaning of integer arguments is discarded when they are passed to bitwise operators. Instead the arguments are treated simply as bit vectors. The bit-vector meaning is preserved when converting between signed and unsigned of the same size. The choice of unsigned over signed as the result type in the mixed case reflects the semantics of standard C.
Bitwise Or Operators¶
The bitwise or operators are predefined as follows:
operator |(a: bool, b: bool): bool
operator |(a: int(?w), b: int(w)): int(w)
operator |(a: uint(?w), b: uint(w)): uint(w)
operator |(a: int(?w), b: uint(w)): uint(w)
operator |(a: uint(?w), b: int(w)): uint(w)
For each of these definitions, the result is computed by applying the logical or operation to the bits of the operands. Chapel allows mixing signed and unsigned integers of the same size when passing them as arguments to bitwise or. No run-time error is issued, even if the apparent sign changes as the required conversions are performed.
Rationale.
The same as for bitwise and (Bitwise And Operators).
Bitwise Xor Operators¶
The bitwise xor operators are predefined as follows:
operator ^(a: bool, b: bool): bool
operator ^(a: int(?w), b: int(w)): int(w)
operator ^(a: uint(?w), b: uint(w)): uint(w)
operator ^(a: int(?w), b: uint(w)): uint(w)
operator ^(a: uint(?w), b: int(w)): uint(w)
For each of these definitions, the result is computed by applying the XOR operation to the bits of the operands. Chapel allows mixing signed and unsigned integers of the same size when passing them as arguments to bitwise xor. No run-time error is issued, even if the apparent sign changes as the required conversions are performed.
Rationale.
The same as for bitwise and (Bitwise And Operators).
Shift Operators¶
This section describes the predefined shift operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
The shift operators are predefined as follows:
operator <<(a: int(8), b): int(8)
operator <<(a: int(16), b): int(16)
operator <<(a: int(32), b): int(32)
operator <<(a: int(64), b): int(64)
operator <<(a: uint(8), b): uint(8)
operator <<(a: uint(16), b): uint(16)
operator <<(a: uint(32), b): uint(32)
operator <<(a: uint(64), b): uint(64)
operator >>(a: int(8), b): int(8)
operator >>(a: int(16), b): int(16)
operator >>(a: int(32), b): int(32)
operator >>(a: int(64), b): int(64)
operator >>(a: uint(8), b): uint(8)
operator >>(a: uint(16), b): uint(16)
operator >>(a: uint(32), b): uint(32)
operator >>(a: uint(64), b): uint(64)
The type of the second actual argument must be any integral type.
The <<
operator shifts the bits of a
left by the integer b
.
The new low-order bits are set to zero.
The >>
operator shifts the bits of a
right by the integer b
.
When a
is negative, the new high-order bits are set to one;
otherwise the new high-order bits are set to zero.
The value of b
must be non-negative.
The value of b
must be less than the number of bits in a
.
Logical Operators¶
This section describes the predefined logical operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
The Logical Negation Operator¶
The logical negation operator is predefined for booleans and integers as follows:
operator !(a: bool): bool
operator !(a: int(?w)): bool
operator !(a: uint(?w)): bool
For the boolean form, the result is the logical negation of the operand. For the integer forms, the result is true if the operand is zero and false otherwise.
The Logical And Operator¶
The logical and operator is predefined over bool type. It returns true if both operands evaluate to true; otherwise it returns false. If the first operand evaluates to false, the second operand is not evaluated and the result is false.
The logical and operator over expressions a
and b
given by
a && b
is evaluated as the expression
if isTrue(a) then isTrue(b) else false
The function isTrue
is predefined over bool type as follows:
proc isTrue(a:bool) return a;
Overloading the logical and operator over other types is accomplished by
overloading the isTrue
function over other types.
The Logical Or Operator¶
The logical or operator is predefined over bool type. It returns true if either operand evaluate to true; otherwise it returns false. If the first operand evaluates to true, the second operand is not evaluated and the result is true.
The logical or operator over expressions a
and b
given by
a || b
is evaluated as the expression
if isTrue(a) then true else isTrue(b)
The function isTrue
is predefined over bool type as described
in The Logical And Operator. Overloading the logical or
operator over other types is accomplished by overloading the isTrue
function over other types.
Relational Operators¶
This section describes the predefined relational operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
Ordered Comparison Operators¶
The “less than” comparison operators are predefined over numeric types as follows:
operator <(a: int(8), b: int(8)): bool
operator <(a: int(16), b: int(16)): bool
operator <(a: int(32), b: int(32)): bool
operator <(a: int(64), b: int(64)): bool
operator <(a: uint(8), b: uint(8)): bool
operator <(a: uint(16), b: uint(16)): bool
operator <(a: uint(32), b: uint(32)): bool
operator <(a: uint(64), b: uint(64)): bool
operator <(a: int(64), b: uint(64)): bool
operator <(a: uint(64), b: int(64)): bool
operator <(a: real(32), b: real(32)): bool
operator <(a: real(64), b: real(64)): bool
The result of a < b
is true if a
is less than b
; otherwise
the result is false.
The “greater than” comparison operators are predefined over numeric types as follows:
operator >(a: int(8), b: int(8)): bool
operator >(a: int(16), b: int(16)): bool
operator >(a: int(32), b: int(32)): bool
operator >(a: int(64), b: int(64)): bool
operator >(a: uint(8), b: uint(8)): bool
operator >(a: uint(16), b: uint(16)): bool
operator >(a: uint(32), b: uint(32)): bool
operator >(a: uint(64), b: uint(64)): bool
operator >(a: int(64), b: uint(64)): bool
operator >(a: uint(64), b: int(64)): bool
operator >(a: real(32), b: real(32)): bool
operator >(a: real(64), b: real(64)): bool
The result of a > b
is true if a
is greater than b
;
otherwise the result is false.
The “less than or equal to” comparison operators are predefined over numeric types as follows:
operator <=(a: int(8), b: int(8)): bool
operator <=(a: int(16), b: int(16)): bool
operator <=(a: int(32), b: int(32)): bool
operator <=(a: int(64), b: int(64)): bool
operator <=(a: uint(8), b: uint(8)): bool
operator <=(a: uint(16), b: uint(16)): bool
operator <=(a: uint(32), b: uint(32)): bool
operator <=(a: uint(64), b: uint(64)): bool
operator <=(a: int(64), b: uint(64)): bool
operator <=(a: uint(64), b: int(64)): bool
operator <=(a: real(32), b: real(32)): bool
operator <=(a: real(64), b: real(64)): bool
The result of a <= b
is true if a
is less than or equal to
b
; otherwise the result is false.
The “greater than or equal to” comparison operators are predefined over numeric types as follows:
operator >=(a: int(8), b: int(8)): bool
operator >=(a: int(16), b: int(16)): bool
operator >=(a: int(32), b: int(32)): bool
operator >=(a: int(64), b: int(64)): bool
operator >=(a: uint(8), b: uint(8)): bool
operator >=(a: uint(16), b: uint(16)): bool
operator >=(a: uint(32), b: uint(32)): bool
operator >=(a: uint(64), b: uint(64)): bool
operator >=(a: int(64), b: uint(64)): bool
operator >=(a: uint(64), b: int(64)): bool
operator >=(a: real(32), b: real(32)): bool
operator >=(a: real(64), b: real(64)): bool
The result of a >= b
is true if a
is greater than or equal to
b
; otherwise the result is false.
The ordered comparison operators are predefined over strings as follows:
operator <(a: string, b: string): bool
operator >(a: string, b: string): bool
operator <=(a: string, b: string): bool
operator >=(a: string, b: string): bool
Comparisons between strings are defined based on the ordering of the character set used to represent the string, which is applied elementwise to the string’s characters in order.
Equality Comparison Operators¶
The equality comparison operators ==
and !=
are predefined
over bool and the numeric types as follows:
operator ==(a: int(8), b: int(8)): bool
operator ==(a: int(16), b: int(16)): bool
operator ==(a: int(32), b: int(32)): bool
operator ==(a: int(64), b: int(64)): bool
operator ==(a: uint(8), b: uint(8)): bool
operator ==(a: uint(16), b: uint(16)): bool
operator ==(a: uint(32), b: uint(32)): bool
operator ==(a: uint(64), b: uint(64)): bool
operator ==(a: int(64), b: uint(64)): bool
operator ==(a: uint(64), b: int(64)): bool
operator ==(a: real(32), b: real(32)): bool
operator ==(a: real(64), b: real(64)): bool
operator ==(a: imag(32), b: imag(32)): bool
operator ==(a: imag(64), b: imag(64)): bool
operator ==(a: complex(64), b: complex(64)): bool
operator ==(a: complex(128), b: complex(128)): bool
operator !=(a: int(8), b: int(8)): bool
operator !=(a: int(16), b: int(16)): bool
operator !=(a: int(32), b: int(32)): bool
operator !=(a: int(64), b: int(64)): bool
operator !=(a: uint(8), b: uint(8)): bool
operator !=(a: uint(16), b: uint(16)): bool
operator !=(a: uint(32), b: uint(32)): bool
operator !=(a: uint(64), b: uint(64)): bool
operator !=(a: int(64), b: uint(64)): bool
operator !=(a: uint(64), b: int(64)): bool
operator !=(a: real(32), b: real(32)): bool
operator !=(a: real(64), b: real(64)): bool
operator !=(a: imag(32), b: imag(32)): bool
operator !=(a: imag(64), b: imag(64)): bool
operator !=(a: complex(64), b: complex(64)): bool
operator !=(a: complex(128), b: complex(128)): bool
The result of a == b
is true if a
and b
contain the same
value; otherwise the result is false. The result of a != b
is
equivalent to !(a == b)
.
The equality comparison operators are predefined over classes as follows:
operator ==(a: object, b: object): bool
operator !=(a: object, b: object): bool
The result of a == b
is true if a
and b
reference the same
storage location; otherwise the result is false. The result of
a != b
is equivalent to !(a == b)
.
Default equality comparison operators are generated for records if the user does not define them. These operators are described in Default Comparison Operators.
The equality comparison operators are predefined over strings as follows:
operator ==(a: string, b: string): bool
operator !=(a: string, b: string): bool
The result of a == b
is true if the sequence of characters in a
matches exactly the sequence of characters in b
; otherwise the
result is false. The result of a != b
is equivalent to !(a == b)
.
Class Operators¶
The keywords owned
, shared
, borrowed
, and unmanaged
act
as a prefix unary operator when specifying the management strategy for a
class type. See Class Types.
The unary postfix operator ?
results in the nilable variant of a
class type. See Nilable Class Types.
The unary postfix operator !
asserts that the receiver is not
storing nil
and borrows from it.
See Nilable Class Types.
Miscellaneous Operators¶
This section describes several miscellaneous operators. These operators can be redefined over different types using operator overloading (Function and Operator Overloading).
The String Concatenation Operator¶
The string concatenation operator +
is predefined for string
arguments and returns a new string that is the concatenation of its
arguments:
operator +(s0: string, s1: string): string
Example (string-concat.chpl).
The code:
var x: string = "hi"; var y: string = " there"; var z = x + y;will cause
z
to be a new string containing the value"hi there"
.
The By Operator¶
The operator by
is predefined on ranges and rectangular domains. It
is described in By Operator for ranges
and Domain Striding for domains.
The Align Operator¶
The operator align
is predefined on ranges and rectangular domains.
It is described in Align Operator for ranges
and Domain Alignment for domains.
The Range Count Operator¶
The operator #
is predefined on ranges. It is described in
Count Operator.
Let Expressions¶
A let expression allows variables to be declared at the expression level and used within that expression. The syntax of a let expression is given by:
let-expression:
'let' variable-declaration-list 'in' expression
The scope of the variables is the let-expression.
Example (let.chpl).
Let expressions are useful for defining variables in the context of an expression. In the code
let x: real = a*b, y = x*x in 1/ythe value determined by
a*b
is computed and converted to type real if it is not already a real. The square of the real is then stored iny
and the result of the expression is the reciprocal of that value.
Conditional Expressions¶
A conditional expression is given by the following syntax:
if-expression:
'if' expression 'then' expression 'else' expression
'if' expression 'then' expression
The conditional expression is evaluated in two steps. First, the
expression following the if
keyword is evaluated. Then, if the
expression evaluated to true, the expression following the then
keyword is evaluated and taken to be the value of this expression.
Otherwise, the expression following the else
keyword is evaluated
and taken to be the value of this expression. In both cases, the
unselected expression is not evaluated.
The ‘else’ clause can be omitted only when the conditional expression is nested immediately inside a for or forall expression. Such an expression is used to filter predicates as described in Filtering Predicates in For Expressions and Filtering Predicates in Forall Expressions, respectively.
Example (condexp.chpl).
This example shows how if-then-else can be used in a context in which an expression is expected. The code
writehalf(8); writehalf(21); writehalf(1000); proc writehalf(i: int) { var half = if (i % 2) then i/2 +1 else i/2; writeln("Half of ",i," is ",half); }produces the output
Half of 8 is 4 Half of 21 is 11 Half of 1000 is 500
For Expressions¶
A for expression is given by the following syntax:
for-expression:
'for' index-var-declaration 'in' iteratable-expression 'do' expression
'for' iteratable-expression 'do' expression
A for expression is an iterator that executes a for loop (The For Loop), evaluates the body expression on each iteration of the loop, and yields each resulting value.
When a for expression is used to initialize a variable, such as
var X = for iterableExpression() do computeValue();
the variable will be inferred to have an array type. The array’s domain
is defined by the iterable-expression
following the same rules as
for promotion, both in the regular case Promotion and in
the zipper case Zipper Promotion.
Filtering Predicates in For Expressions¶
A conditional expression that is immediately enclosed in a for expression and does not require an else clause filters the iterations of the for expression. The iterations for which the condition does not hold are not reflected in the result of the for expression.
When a for expression with a filtering predicate is captured into a variable, the resulting array has a 0-based one-dimensional domain.
Example (yieldPredicates.chpl).
The code
var A = for i in 1..10 do if i % 3 != 0 then i;declares an array A that is initialized to the integers between 1 and 10 that are not divisible by 3.