forked from HaxeFoundation/HaxeManual
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path05-expressions.tex
466 lines (308 loc) · 19.9 KB
/
05-expressions.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
\chapter{Expressions}
\label{expression}
Expressions in Haxe define what a program \emph{does}. Most expressions are found in the body of a \tref{method}{class-field-method}, where they are combined to express what that method should do. This section explains the different kinds of expressions. Some definitions help here:
\define{Name}{define-name}{A general name may refer to
\begin{itemize}
\item a type,
\item a local variable,
\item a local function or
\item a field.
\end{itemize}}
\define{Identifier}{define-identifier}{Haxe identifiers start with an underscore \expr{_}, a dollar \expr{\$}, a lower-case character \expr{a-z} or an upper-case character \expr{A-Z}. After that, any combination and number of \expr{_}, \expr{A-Z}, \expr{a-z} and \expr{0-9} may follow.\\
Further limitations follow from the usage context, which are checked upon typing:
\begin{itemize}
\item Type names must start with an upper-case letter \expr{A-Z} or an underscore \expr{_}.
\item Leading dollars are not allowed for any kind of \tref{name}{define-name} (dollar-names are mostly used for \tref{macro reification}{macro-reification}).
\end{itemize}}
\section{Blocks}
\label{expression-block}
A block in Haxe starts with an opening curly brace \expr{\{} and ends with a closing curly brace \expr{\}}. A block may contain several expressions, each of which is followed by a semicolon \expr{;}. The general syntax is:
\begin{lstlisting}
{
expr1;
expr2;
...
exprN;
}
\end{lstlisting}
The value and, by extension, the type of a block-expression are equal to the value and the type of the last sub-expression.
Blocks can contain local variables declared by \tref{\expr{var} expression}{expression-var} as well as local functions declared by \tref{\expr{function} expressions}{expression-function}. These are available within the block and within sub-blocks but not outside the block. Also, they are available only after their declaration. The following example uses \expr{var} but the same rules apply to \expr{function} usage:
\begin{lstlisting}
{
a; // error, a is not declared yet
var a = 1; // declare a
a; // ok, a was declared
{
a; // ok, a is available in sub-blocks
}
// ok, a is still available after
// sub-blocks
a;
}
a; // error, a is not available outside
\end{lstlisting}
At run-time, blocks are evaluated from top to bottom. Control flow (e.g. \tref{exceptions}{expression-try-catch} or \tref{return expressions}{expression-return}) may leave a block before all expressions are evaluated.
\section{Constants}
\label{expression-constants}
The Haxe syntax supports the following constants:
\begin{description}
\item[Int:] An \tref{integer}{define-int}, such as \expr{0}, \expr{1}, \expr{97121}, \expr{-12}, \expr{0xFF0000}.
\item[Float:] A \tref{floating point number}{define-float}, such as \expr{0.0}, \expr{1.}, \expr{.3}, \expr{-93.2}.
\item[String:] A \tref{string of characters}{define-string}, such as \expr{""}, \expr{"foo"}, \expr{''}, \expr{'bar'}.
\item[true,false:] A \tref{boolean}{define-bool} value.
\item[null:] The null value.
\end{description}
Furthermore, the internal syntax structure treats \tref{identifiers}{define-identifier} as constants. This may be relevant when working with \tref{macros}{macro}.
\section{Binary Operators}
\label{expression-binops}
\section{Unary Operators}
\label{expression-unops}
\section{Array Declaration}
\label{expression-array-declaration}
Arrays are initialized by enclosing comma \expr{,} separated values in brackets \expr{[]}. A plain \expr{[]} represents the empty array whereas \expr{[1, 2, 3]} initializes an array with three elements \expr{1}, \expr{2} and \expr{3}.
The generated code may be less concise on platforms that do not support array initialization. Essentially, such initialization code then looks like the following:
\begin{lstlisting}
var a = new Array();
a.push(1);
a.push(2);
a.push(3);
\end{lstlisting}
This should be taken into consideration when deciding if a function should be \tref{inlined}{class-field-inline} as it may inline more code than is visible in the syntax.
Advanced initialization techniques are described in \Fullref{lf-array-comprehension}.
\section{Object Declaration}
\label{expression-object-declaration}
Object declaration begins with an opening curly brace \expr{\{} after which \expr{key:value}-pairs separated by comma \expr{,} follow and ends in a closing curly brace \expr{\}}.
\begin{lstlisting}
{
key1:value1,
key2:value2,
...
keyN:valueN
}
\end{lstlisting}
Further details of object declaration are described in the section about \tref{anonymous structures}{types-anonymous-structure}.
\section{Field Access}
\label{expression-field-access}
Field access is expressed by using the dot \expr{.} followed by the name of the field.
\begin{lstlisting}
object.fieldName
\end{lstlisting}
This syntax is also used to access types within packages in the form of \expr{pack.Type}.
The typer ensures that an accessed field actually exist and may apply transformations depending on the nature of the field. If a field access is ambiguous, understanding the \tref{resolution order}{type-system-resolution-order} may help.
\section{Array Access}
\label{expression-array-access}
Array access is expressed by using an opening bracket \expr{[} followed by the index expression and a closing bracket \expr{]}.
\begin{lstlisting}
expr[indexExpr]
\end{lstlisting}
This notation is allowed with arbitrary expressions, but at typing level only certain combinations are admitted:
\begin{itemize}
\item \expr{expr} is of \type{Array} or \type{Dynamic} and \expr{indexExpr} is of \type{Int}
\item \expr{expr} is an \tref{abstract type}{types-abstract} which defines a matching \tref{array access}{types-abstract-array-access}
\end{itemize}
\section{Function Call}
\label{expression-function-call}
Functions calls consist of an arbitrary subject expression followed by an opening parenthesis \expr{(}, a comma \expr{,} separated list of expressions as arguments and a closing parenthesis \expr{)}.
\begin{lstlisting}
subject(); // call with no arguments
subject(e1); // call with one argument
subject(e1, e2); // call with two arguments
// call with multiple arguments
subject(e1, ..., eN);
\end{lstlisting}
\section{var}
\label{expression-var}
The \expr{var} keyword allows declaring multiple variables, separated by comma \expr{,}. Each variable has a valid \tref{identifier}{define-identifier} and optionally a value assignment following the assignment operator \expr{=}. Variables can also have an explicit type-hint.
\begin{lstlisting}
var a; // declare local a
var b:Int; // declare variable b of type Int
// declare variable c, initialized to value 1
var c = 1;
// declare variable d and variable e
// initialized to value 2
var d,e = 2;
\end{lstlisting}
The scoping behavior of local variables is described in \Fullref{expression-block}.
\section{Local functions}
\label{expression-function}
Haxe supports first-class functions and allows declaring local functions in expressions. The syntax follows \tref{class field methods}{class-field-method}:
\haxe{assets/LocalFunction.hx}
We declare \expr{myLocalFunction} inside the \tref{block expression}{expression-block} of the \expr{main} class field. It takes one argument \expr{i} and adds it to \expr{value}, which is defined in the outside scope.
The scoping is equivalent to that of \tref{variables}{expression-var} and for the most part writing a named local function can be considered as being equal to assigning an unnamed local function to a local variable:
\begin{lstlisting}
var myLocalFunction = function(a) { }
\end{lstlisting}
However, there are some differences relating to type parameters and the position of the function. We speak of an ``lvalue'' function if it is not assigned to anything upon its declaration, and an ``rvalue'' function otherwise.
\begin{itemize}
\item Lvalue functions require a name and can have \tref{type parameters}{type-system-type-parameters}.
\item Rvalue functions may have a name, but cannot have type parameters.
\end{itemize}
\section{new}
\label{expression-new}
The \expr{new} keyword signals that a \tref{class}{types-class-instance} or an \tref{abstract}{types-abstract} is being instantiated. It is followed by the \tref{type path}{define-type-path} of the type which is to be instantiated. It may also list explicit \tref{type parameters}{type-system-type-parameters} enclosed in \expr{<>} and separated by commas \expr{,}. After an opening parenthesis \expr{()} follow the constructor arguments, again separated by commas \expr{,}, with a closing parenthesis \expr{)} at the end.
\haxe{assets/New.hx}
Within the \expr{main} method we instantiate an instance of \type{Main} itself with an explicit type parameter \type{Int} and the arguments \expr{12} and \expr{"foo"}. As we can see, the syntax is very similar to the \tref{function call syntax}{expression-function-call} and it is common to speak of ``constructor calls''.
\section{for}
\label{expression-for}
Haxe does not support traditional for-loops known from C. Its \expr{for} keyword expects an opening parenthesis \expr{(}, then a variable identifier followed by the keyword \expr{in} and an arbitrary expression used as iterating collection. After the closing parenthesis \expr{)} follows an arbitrary loop body expression.
\begin{lstlisting}
for (v in e1) e2;
\end{lstlisting}
The typer ensures that the type of \expr{e1} can be iterated over, which is typically the case if it has an \expr{iterator} method returning an \type{Iterator<T>}, or if it is an \type{Iterator<T>} itself.
Variable \expr{v} is then available within the body od the loop \expr{e2} and holds the value of the individual elements of collection \expr{e1}.
The type of a \expr{for} expression is always \type{Void}, meaning it has no value and cannot be used as right-side expression.
The control flow of loops can be affected by \tref{\expr{break}}{expression-break} and \tref{\expr{continue}}{expression-continue} expressions.
\section{while}
\label{expression-while}
A normal while loop starts with the \expr{while} keyword, followed by an opening parenthesis \expr{(}, the condition expression and a closing parenthesis \expr{)}. After that follows the body expression of the loop:
\begin{lstlisting}
while(condition) expression;
\end{lstlisting}
The condition expression has to be of type \type{Bool}.
Upon each iteration, the condition expression is evaluated. If it evaluates to \expr{false}, the loop stops, otherwise it evaluates the loop body expression.
\haxe{assets/While.hx}
This kind of while-loop is not guaranteed to evaluate the loop body expression at all: If the condition does not hold from the start, it is never evaluated. This is different for \tref{do-while loops}{expression-do-while}.
\section{do-while}
\label{expression-do-while}
A do-while loop starts with the \expr{do} keyword followed by the loop body expression. After that follows the \expr{while} keyword, an opening parenthesis \expr{(}, the condition expression and a closing parenthesis \expr{)}:
\begin{lstlisting}
do expression while(condition);
\end{lstlisting}
The condition expression has to be of type \type{Bool}.
As the syntax suggests, the loop body expression is always evaluated at least once, unlike \tref{while}{expression-while} loops.
\section{if}
\label{expression-if}
Conditional expressions come in the form of a leading \expr{if} keyword, a condition expression enclosed in parentheses \expr{()} and an expression to be evaluated in case the condition holds:
\begin{lstlisting}
if (condition) expression;
\end{lstlisting}
The condition expression has to be of type \type{Bool}.
Optionally, \expr{expression} may be followed by the \expr{else} keyword as well as another expression to be evaluated if the condition does not hold:
\begin{lstlisting}
if (condition) expression1 else expression2;
\end{lstlisting}
Here, \expr{expression2} may consist of another \expr{if} expression:
\begin{lstlisting}
if (condition1) expression1
else if(condition2) expression2
else expression3
\end{lstlisting}
If the value of an \expr{if} expression is required, e.g. for \expr{var x = if(condition) expression1 else expression2}, the typer ensures that the types of \expr{expression1} and \expr{expression2} \tref{unify}{type-system-unification}. If no \expr{else} expression is given, the type is inferred to be \type{Void}.
\section{switch}
\label{expression-switch}
A basic switch expression starts with the \expr{switch} keyword and the switch subject expression as well as the case expressions between curly braces \expr{\{\}}. Case expressions either start with the \expr{case} keyword and are followed by a pattern expression, or consist of the \expr{default} keyword. In both cases a colon \expr{:} and an optional case body expression follow:
\begin{lstlisting}
switch subject {
case pattern1: case-body-expression-1;
case pattern2: case-body-expression-2;
default: default-expression;
}
\end{lstlisting}
Case body expressions never ``fall through'', so the \tref{\expr{break}}{expression-break} keyword is not supported in Haxe.
Switch expressions can be used as value. In that case the types of all case body expressions and the default expression must \tref{unify}{type-system-unification}.
Further details on the syntax of pattern expressions are outlined in \Fullref{lf-pattern-matching}.
\section{try/catch}
\label{expression-try-catch}
Haxe allows catching values using its \expr{try/catch} syntax:
\begin{lstlisting}
try try-expr
catch(varName1:Type1) catch-expr-1
catch(varName2:Type2) catch-expr-2
\end{lstlisting}
If during run-time the evaluation of \expr{try-expression} causes a \tref{\expr{throw}}{expression-throw}, it can be caught by any subsequent \expr{catch} block. These blocks consist of
\begin{itemize}
\item a variable name which holds the thrown value,
\item an explicit type annotation which determines which types of values to catch, and
\item the expression to execute in that case.
\end{itemize}
Haxe allows throwing and catching any kind of value. It is not limited to types inheriting from a specific exception or error class. Catch blocks are checked from top to bottom with the first one whose type is compatible with the thrown value being picked.
This process has many similarities to the compile-time \tref{unification}{type-system-unification} behavior. However, since the check has to be done at run-time, there are some restrictions:
\begin{itemize}
\item The type must exist at run-time: \tref{Class instances}{types-class-instance}, \tref{enum instances}{types-enum-instance}, \tref{abstract core types}{types-abstract-core-type} and \tref{Dynamic}{types-dynamic}.
\item Type parameters can only be \tref{Dynamic}{types-dynamic}.
\end{itemize}
\section{return}
\label{expression-return}
A \expr{return} expression can come with or without a value expression:
\begin{lstlisting}
return;
return expression;
\end{lstlisting}
It leaves the control-flow of the innermost function it is declared in, which has to be distinguished when \tref{local functions}{expression-function} are involved:
\begin{lstlisting}
function f1() {
function f2() {
return;
}
f2();
expression;
}
\end{lstlisting}
The \expr{return} leaves local function \expr{f2}, but not \expr{f1}, meaning \expr{expression} is still evaluated.
If \expr{return} is used without a value expression, the typer ensures that the return type of the function it returns from is of \type{Void}. If it has a value expression, the typer \tref{unifies}{type-system-unification} its type with the return type (explicitly given or inferred by previous \expr{return} expressions) of the function it returns from.
\section{break}
\label{expression-break}
The \expr{break} keyword leaves the control flow of the innermost loop (\expr{for} or \expr{while}) it is declared in, stopping further iterations:
\begin{lstlisting}
while(true) {
expression1;
if (condition) break;
expression2;
}
\end{lstlisting}
Here, \expr{expression1} is evaluated for each iteration but as soon as \expr{condition} holds, \expr{expression2} is not evaluated anymore.
The typer ensures that it appears only within a loop. The \expr{break} keyword in \tref{\expr{switch} cases}{expression-switch} is not supported in Haxe.
\section{continue}
\label{expression-continue}
The \expr{continue} keyword ends the current iteration of the innermost loop (\expr{for} or \expr{while}) it is declared in, causing the loop condition to be checked for the next iteration:
\begin{lstlisting}
while(true) {
expression1;
if(condition) continue;
expression2;
}
\end{lstlisting}
Here, \expr{expression1} is evaluated for each iteration but if \expr{condition} holds, \expr{expression2} is not evaluated for the current iteration. Unlike \expr{break}, iterations continue.
The typer ensures that it appears only within a loop.
\section{throw}
\label{expression-throw}
Haxe allows throwing any kind of value using its \expr{throw} syntax:
\begin{lstlisting}
throw expr
\end{lstlisting}
A value which is thrown like this can be caught by \tref{\expr{catch} blocks}{expression-try-catch}. If no such block catches it, the behavior is target-dependent.
\section{cast}
\label{expression-cast}
Haxe allows two kinds of casts:
\begin{lstlisting}
cast expr; // unsafe cast
cast (expr, Type); // safe cast
\end{lstlisting}
\subsection{unsafe cast}
\label{expression-cast-unsafe}
Unsafe casts are useful to subvert the type system. The compiler types \expr{expr} as usual and then wraps it in a \tref{monomorph}{types-monomorph}. This allows the expression to be assigned to anything.
Unsafe casts do not introduce any \tref{dynamic}{types-dynamic} types, as the following example shows:
\haxe{assets/UnsafeCast.hx}
Variable \expr{i} is typed as \type{Int} and then assigned to variable \expr{s} using the unsafe cast \expr{cast i}. This causes \expr{s} to be of an unknown type, a monomorph. Following the usual rules of \tref{unification}{type-system-unification}, it can then be bound to any type such as \type{String} in this example.
These casts are called "unsafe" because the run-time behavior for invalid casts is not defined. While most \tref{dynamic targets}{define-dynamic-target} are likely to work, it might lead to undefined errors on \tref{static targets}{define-static-target}.
Unsafe casts have little to no run-time overhead.
\subsection{safe cast}
\label{expression-cast-safe}
Unlike \tref{unsafe casts}{expression-cast-unsafe}, the run-time behavior in case of a failing cast is defined for safe casts:
\haxe{assets/SafeCast.hx}
In this example we first cast a class instance of type \type{Child1} to \type{Base}, which succeeds because \type{Child1} is a \tref{child class}{types-class-inheritance} of \type{Base}. We then try to cast the same class instance to \type{Child2} which is not allowed because instances of \type{Child2} are not instances of \type{Child1}.
The Haxe compiler guarantees that an exception of type \type{String} is \tref{thrown}{expression-throw} in this case. This exception can be caught using a \tref{\expr{try/catch} block}{expression-try-catch}.
Safe casts have a run-time overhead. It is important to understand that the compiler already generates type checks. Hence, it is redundant to add manual checks, e.g. using \expr{Std.is}. The intended usage is to try the safe cast and catch the \type{String} exception.
\section{type check}
\label{expression-type-check}
\since{3.1.0}
It is possible to employ compile-time type checks using the following syntax:
\begin{lstlisting}
(expr : type)
\end{lstlisting}
The parentheses are mandatory. Unlike \tref{safe casts}{expression-cast-safe}, this construct has no run-time impact. It has two compile-time implications:
\begin{enumerate}
\item \tref{Top-down inference}{type-system-top-down-inference} is used to type \expr{expr} with type \expr{type}.
\item The resulting typed expression is \tref{unified}{type-system-unification} with type \expr{type}.
\end{enumerate}
This has the usual effect of both operations such as the given type being used as expected type when performing \tref{unqualified identifier resolution}{type-system-resolution-order} and the unification checking for \tref{abstract casts}{types-abstract-implicit-casts}.