Basic Operators¶
CDot supports most of the basic arithmetic, logical and assignment operators you are probably familiar with from other languages. Unlike many of those other languages though, every operator in CDot is actually just a normal function or method that can be called with operator syntax. You can also define your own operators with custom spellings, for more information on how to do that see Functions.
Operators come in three forms:
Infix operators, which operate on two values and are written between the two
Prefix operators, which operate on one value and are written before it
Postfix operators, which operate on one value and are written after it
Arithmetic Operators¶
All numeric types (i.e. integer and floating point types) support the familiar arithmetic infix operators +
, -
, *
and /
. In contrast to their C equivalents, these operators check for overflow and exit the application with a fatal error when overflow (or division by zero) is encountered. You can opt in to the usual behavior of silently allowing overflow by using &
-prefixed variants of these operators:
1 2 3 | let add1 = 312 + 328 // OK
let add2: Int8 = 127 + 1 // fatal error: unexpected integer overflow!
let add3: Int8 = 127 &+ 1 // OK: yields -128
|
In addition to the default arithmetic operators, CDot also supports remainder and exponentiation operators, which are spelled %
and **
, respectively.
let rem = 125 % 12 // Yields 5
let exp = 10 ** 5 // Yields 100,000
Prefix Arithmetic Operators¶
There are two additional unary arithmetic operators, +
and -
. The prefix -
operator can be used to negate a numeric value, while the prefix +
operator has no real purpose except for providing symmetry:
1 2 3 | let negOne = -1
let stillNegOne = +negOne
let posOne = -stillNegOne
|
Assignment Operators¶
As you have seen in previous examples, variables can be assigned a new value using the assignment operator =
. To avoid accidental confusion between this operator and the equality comparison operator ==
, assignment does not return a value in CDot:
1 2 3 4 | var v = 210
v = 32
if v = 32 {} // error: assignment does not yield a value
|
Cdot also supports compound assignment operators for all of the arithmetic operators, which combine an arithmetic computation with an assignment. These are spelled as {op}=
and can be used like this:
1 2 3 4 | var v = 128
v *= 3 // Multiply v by three and assign the result back to v
v -= 10 // Subtract ten from v and assign the result back to v
print(v) // Prints "374"
|
Comparison Operators¶
Most types support the equality comparison operators ==
and !=
. Numeric types additionally support the comparison operators <
, >
, <=
and >=
. All of these yield a boolean value and have the expected behavior.
1 2 3 4 5 6 7 8 | if 3 == 4 {
print("well that's odd") // This will never run
}
var a = 3 > 4 // a is false
var b = 3 < 4 // b is true
var c = 3 <= 3 // c is true
var d = 8 >= 9 // d is false
|
Note
All of these operators are actually implemented using another operator <=>
, commonly called the spaceship operator. This operator is required to return a negative value if the left-hand operand is smaller than the right, zero if they are equal, and a positive value if the left operand is greater. Implementing this operator for your own types is actually enough to get all six other comparison operators.
There are two additional comparison operators, ===
and !==
, which are called the identity comparison operators. These can only be used on values of class type, and determine whether or not two values refer to the same instance of a class:
1 2 3 4 5 6 | let a = MyClass()
let b = MyClass()
let c = a
print(a === c) // Prints "true"
print(b === c) // Prints "false"
|
Logical Operators¶
Logical operators operate on boolean values. There are three builtin logical operators:
Logical Not
!
, a prefix operatorLogical And
&&
, an infix operatorLogical Or
||
, an infix operator
The logical not operator is used to flip the value of a Bool
, which means that it turns a true
into a false
and vice versa.
1 2 3 | if !condition {
// do something if condition is false
}
|
The logical and and or operators are special because they use short-circuiting, which means that the right-hand side expression will only be evaluated if it is necessary to determine the result of the expression. The &&
operator will evaluate to true
if both operands evaluate to true
, and will only evaluate the right-hand side expression if the left-hand side evaluated to true
. Similarly, the ||
operator will evaluate to true
if either of the operands does, and will only evaluate the right-hand side if the left was not already true
.
1 2 3 4 5 6 7 8 9 | // `condition2` is only called if `condition1` returns true
if condition1() && condition2() {
// do something...
}
// `condition4` is only called if `condition3` returns false
if condition3() || condition4() {
// do something...
}
|
Bitwise Operators¶
Bitwise operators operate on the individual bits of an integer value. There are several of them in CDot:
Bitwise Not
~
, a prefix operatorBitwise Or
|
, an infix operatorBitwise And
&
, an infix operatorBitwise Xor
^
, an infix operator
The bitwise not operator flips the value of each individual bit:
var binaryValue = 0b0100_1010
print((~binaryValue).toString(base: 2)) // Prints 0b1011_0101
The remaining bitwise operators compare the bits of two values and set the result bit according to their specific behaviour:
|
sets the result bit if either of the operand bits were one
&
sets the result bit if both of the operand bits were one
^
sets the result bit if exactly one of the operand bits is one
Here’s an example of how they work in action:
1 2 3 4 5 6 | var value1 = 0b1010
var value2 = 0b1100
print((value1 & value2).toString(base: 2)) // Prints "0b1000"
print((value1 | value2).toString(base: 2)) // Prints "0b1110"
print((value1 ^ value2).toString(base: 2)) // Prints "0b0110"
|
Optional Operators¶
CDot provides additional operators to make dealing with optional values more convenient:
Force Unwrap
!
, a postfix operatorSafe Unwrap
??
, an infix operator
The force unwrap operator is used when you are certain that an optional contains a value. It will return the contained value or trap with a runtime failure if there is none.
1 2 3 4 5 | var opt1: Int? = 3
print(opt1!) // Prints "3"
var opt2: Int? = none
print(opt2!) // fatal error: force unwrapping a `none` value!
|
Because this operator has the ability to trap, you should only use it when you are absolutely positive that an operation returns a value. In cases where you are not sure, you can use the safe unwrap operator instead. This operator will evaluate and return the right-hand side value if the left-hand side optional is none
:
1 2 3 4 5 | let defaultValue = 17
var optionalValue: Int? = none
var safeValue = optionalValue ?? defaultValue
print(safeValue) // Prints "17"
|
This operator is also short-circuiting, which means that the default value is only evaluated if the optional value is none
.
Note
The short-circuiting behavior of the ??
operator is made possible by a special attribute, which will be explained in more detail later.
Range-Formation Operators¶
CDot also provides three convenience operators for forming ranges, one of which you have already seen in previous examples. These operators are a shorthand for forming range expressions, and are commonly used in for-in
loops. The three range formation operators are:
Exclusive Range Operator
..
, an infix operatorInclusive Range Operator
..=
, an infix operatorHalf Open Range Operator
..
, a postfix operator
The exclusive range operator forms a range from the left-hand side up to the right-hand side (exclusive), while the inclusive version forms a range that includes the upper bound. The half open range operator is special in that it does not provide an upper bound, instead continuing on forever.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Exclusive range, prints "0 1 2 3 4"
for i in 0..5 {
print(i)
}
// Inclusive range, prints "0 1 2 3 4 5"
for i in 0..=5 {
print(i)
}
// Half open range, prints "0 1 2 3 4 5 6 7 ..."
for i in 0.. {
print(i)
}
|