Expressions are composed of operators and operands and evaluate to a single final value.
Kal supports all arithmetic, logical, relational, bitwise and unary operators.
Here's a table highlighting all operators and their precedence (from highest to lowest):
| Operator | Precedence |
|---|
| as | 16 |
| ?? | 15 |
| - ! ~ | 14 |
| ** | 13 |
| * / % | 12 |
| + - | 11 |
| << >> | 10 |
| < <= > >= | 9 |
| == != | 8 |
| & | 7 |
| ^ | 6 |
| | | 5 |
| && | 4 |
| || | 3 |
| ? : | 2 |
| := | 1 |
Let's look at these operators grouped by their types.
Starting with arithmetic operators:
stdout
(8 + 5) " " ;; addition
(8 - 5) " " ;; subtraction
(8 * 5) " " ;; multiplication
(8 / 5) " " ;; division
(8 % 5) " " ;; modulus
(8 ** 5) "\n". ;; power arithmetic.kal
Relational operators evaluate to 0 or 1, analogous to False and True based on the expressed relation.
stdout
(5 == 5) " " ;; equal to
(5 != 13) " " ;; not equal to
(5 < 13) " " ;; less than
(5 > 13) " " ;; greater than
(5 >= 5) " " ;; greater than or equal to
(13 <= 13) "\n". ;; less than or equal to relational.kal
Logical operators evaluate to 0 or 1, analogous to False and True based on the result of the boolean operation.
stdout
(!0) " " ;; logical not
(!1) " "
(0 || 0) " " ;; logical or
(0 || 1) " "
(1 || 1) " "
(0 && 0) " " ;; logical and
(0 && 1) " "
(1 && 1) "\n". logical.kal
Bitwise operators:
stdout
(~5) " " ;; bitwise not
(5 & 13) " " ;; bitwise and
(5 | 13) " " ;; bitwise or
(13 << 2) " " ;; left shift
(13 >> 2) "\n". ;; right shift bitwise.kal
The
as
operator is used to type cast values as you have already seen.
The null coalescing operator,
??
is useful when you are not sure if the given variable is
null
or not. It takes a fallback value which is used if the varriable turns out to be
null
.
var value = null.
stdout (value ?? "Absent") "\n".
nullCoalescing.kal
Here, the variable
value
is
null
, therefore
??
uses
"Absent"
as the fallback value.
Null coalescing does not work if a variable that it's acting upon is not set to
null
.
var value = "Present".
stdout (value ?? "Absent") "\n".
nullCoalescing.kal
Ternary operators perform conditional operations based on the boolean expression that they act upon.
They take two expressions, first of which is evaluated when the condition results in True and the other when the condition results in False.
var value = (1 == 1) ? "Yes" : "No".
stdout value "\n".
ternary.kal
In this example the condition
(1 == 1)
results in True, therefore the expression associated with
?
is evaluated. The expression associated with
:
is discarded.
Similarly, the expression associated with : is evaluated when the condition results in False.
var value = (1 == 2) ? "Yes" : "No".
stdout value "\n".
ternary.kal
The ternary operators can be expanded multi-line for readability.
var value = (1 == 2)
? "Yes"
: "No".
stdout value "\n". expandedTernary.kal
You can optionally use
?
and
:
operators individually.
var value = (1 == 1) ? "Yes".
stdout value "\n".
partialTernary.kal
If the condition was evaluated to False, a fallback value 0 would have been returned.
var value = (1 == 2) : "No".
stdout value "\n".
partialTernary.kal
If the condition was evaluated to True, a fallback value 1 would have been returned.
The Walrus operator,
:=
allows you to initialize a variable inside an expression.
var value = (a := 10) * 2.
stdout "a = " a "\n".
stdout "value = " value "\n".
walrus.kal
The expression
(a := 10)
itself results in 10, the right hand value of the walrus operator once the variable is initialized.
Multiple variables can be initialized in this way.
var value = (a := 10) * (b := 20).
stdout "a = " a "\n".
stdout "b = " b "\n".
stdout "value = " value "\n".
multipleWalrus.kal
You can have complex expressions as well as nesting with the walrus operator.
var value =
(a := (7 + (x := 3)))
*
(b := (5 * 4)).
stdout "a = " a "\n"
"b = " b "\n"
"x = " x "\n"
"value = " value "\n". complexWalrus.kal
You may have noticed the excessive use of parentheses.
They override the order of precedence and allow sub-expression to be evaluated prior.
*
operator has a higher order of precedence than the
+
operator.
var value = 1 + 2 * 3.
stdout "value = " value "\n".
precedence.kal
Here, sub-expression
2 * 3
is evaluated first and the result is added to 1 giving 7 as the final value.
The sub-expression
1 + 2
can be made to evaluate first by wrapping it with parentheses.
var value = (1 + 2) * 3.
stdout "value = " value "\n".
parenPrecedence.kal
Now,
(1 + 2)
is evaluated first and the result is multiplied with 3 giving 9 as the final value.
Parentheses are also strictly required when you are passing expressions as arguments to statements.
stdout 5 + 8 "\n". ;; error
exprArg.kal
Wrap the expression with parentheses.
Values returned by statements are directly assigned to variables.
These values can't be used in an expression directly on an immediate basis.
Kal allows you to convert a statement into an expression so that other operators and operands can use the return value immediately.
Let's consider two statements
first
and
last
, which return the first and last elements of a list respectively.
var data = [30, 45, 60].
first data -> f.
last data -> l.
stdout "First = " f "\n"
"Last = " l "\n".
statement.kal
Observe how return values or
first
and
last
can be used only after their values are assigned to
f
and
l
.
This example can be compacted by converting those statements into expressions by wrapping it with
$(
and
)
. These expressions result in the final return value and hence don't require the target operator (
->
).
var data = [30, 45, 60].
stdout
"First = " $(first data) "\n"
"Last = " $(last data) "\n".
statementExpr.kal
These expressions can used as a part of a larger expression.
var data = [30, 45, 60].
var sum = $(first data) + $(last data).
stdout "Sum = " sum "\n".
statementExprComplex.kal
Multiple strings can be concatenated using the
+
operator.
var message = "Hello" + " " + "Kal!".
stdout message "\n".
concatenation.kal
Similarly, strings can also be multiplied using the
*
operator.
var line = "-" * 5.
stdout line "\n"
"Boxed\n"
line "\n".
strMul.kal
The
*
operator extends its use case to lists by allowing you to generate list by the required length.
var list = [1] * 3.
stdout list "\n".
listMul.kal
It's even more useful when you want to generated nested lists.
var list = [[1] * 3] * 3.
stdout list "\n".
nestedListMul.kal