Flow Control
Constructs to direct the flow of program execution.
Conditional branching
The basic if statement works as expected, but the condition is not enclosed in (). There can only be a conditional expression following the if, so parsing of the condition stops when a newline is encountered or a comment.
All the if options in one example.
if true // is it true?
conditional_code
else if false // or is it false?
never_get_here
else // or is it neither?
other_code
No assignment is permitted inside the
ifconditional expression.
Alternate / inline if:
if true // conventional if statement
...
else
...
true ? ... : ... // ternary operator
true ? ... // ternary without 'else'
Allow in keyword in if statement?
arr := (1, 2, 3, 4, 5)
x := 2
if x in arr // can x be found in arr?
... // true
Parenthesis
When parenthesis are used they are always part of the expression, not of the if statement. There is always a space after the if statement - this is what sets it apart from a function call.
if (42 = 42) // if statement (+ space)
...
if(42) // function call (no space)
Note that if is a reserved keyword and no named language element can have the same name as a reserved keyword. Maybe have an escape character?
Range Conditions
a := 42
if a in [0..100] // true
...
a := 42
if a not in [0..100] // false
if not a in [0..100] // ?
...
If Expressions
TBD
Use the if keyword inside an expression.
a := 42
// newline/indent syntax does not work for expressions
v := if a > 42 -> 101 else a
v := a > 42 ? 101 : a // same
// more complex expressions?
v := if a > 42 -> 101 else if a < 10 -> 0 else a * a
Return
To return a value from a function.
Return Expressions
TBD
The idea here is that the last expression value in the function body is automatically the return (type and) value. See this used in functional languages, but also Rust.
fn1: (): Bool
true
fn2: (p: U8): Bool
p = 42
fn3: (): Bool
fn1()
Yield
todo
Co-routines.
Loops
A loop is an essential control mechanism in directing the execution flow of the code. There is only one keyword for making a loop: loop.
The simplest form is an endless loop:
loop
endless_loop
A ‘while loop’ just adds a condition to the statement:
loop false
never_get_here
loop true
endless_loop
A do-until loop is not supported but can be easily constructed. A do-while loop has to be converted to a do-until loop (invert the condition).
a :=^ 42 // mutable
loop
// a = 42, 41, 40, 39 ... 3, 2, 1
a -= 1
// until condition
if a =< 0 -> break
Loop a certain number of times:
loop [0..10]
loop_10_times
A ‘for’ or ‘for-each’ loop is constructed using a Range.
// n is mutable - but it's not explicit!
loop n in [0..10]
for_n_is_0_to_9
Separate the number of times to loop from the variable value used in the code?
loopcannot be used with a range condition (as used in if). No way to tell the difference. Or do we need a different syntax for iteration?
// explicit to differ from range-condition
loop n in [0..10].GetEnumerator()
foreach_n_0_to_9
// or range condition in () ?
loop (n in [0..10])
n -= 1
Loop a number of times
loop 42
do_this_42_times
c := 42
loop c
do_this_42_times // c is available (value=42)
What happens when c is modified inside the loop?
Loop or else
TBD: Most cases can probably be refactored into simpler code..?
x := 0
loop n in [0..x]
... // empty loop
else
log("Nothing to see here...")
Reverse loop
loop n in -[0..5]
n_from_4_to_0
// or using step?
loop n in [0..5:-1]
...
TBD
Loop with more than one range/iter? How to control if ‘ranges’ are repeated or first one done ends the loop?
loop w in -[0..5], h in [0..10]
w_makes_two_rounds_and_h_one
w_makes_one_round_and_h_half
loop w in -[0..5] and h in [0..10]
w_makes_two_rounds_and_h_one
loop w in -[0..5] or h in [0..10]
w_makes_one_round_and_h_half
TBD
Access index during in-loop.
// n = value
// i = index
loop (n, i) in [0..5]
// n = i
Nested loops
loop h in [0..10]
loop w in [0..5]
w_times_h
Loop with lambda?
loop i in [0..10]
(i) -> log("Now at {i}.`n")
// short syntax?
loop [0..10] (i) -> log("Now at {i}.`n")
Loop with Function?
loop i in [0..10]
LogInt(i)
// short syntax?
loop [0..10] -> LogInt
Custom Iterator function (.NET Enumerable/Enumerator)
// iterator function
loop n in Iter()
work_with_n
TBD implicit casting?
// iterator function
loop n: OtherType in Iter()
work_with_n_of_OtherType
TBD Async loops?
// async iterator function
loop n in IterAsync()
work_with_n_awaited
TBD loops with CancellationTokens?
ct: CancellationToken = ...
// iterator function
loop n in Iter() and ct?
work_with_n
When a type has the ? operator overloaded it can be used as a boolean expression by adding the ? to the end.
Loop Expressions
loop // endless loop
loop <integer> // loop count
loop <var> in [-]<range> // (reverse) for-loop
loop <var> in <iter> // for-each loop
(loop <expression>) // access loop object
(loop <expression>).AsParallel() // call loop object function
Expressions can be combined using the and and or keywords.
The and keyword requires both sides to finish. How to control if the shorter/faster side is to be restarted?
loop n in [0..3] and s in [2..9]
...
The ‘or’ keyword allows the fastest side to end the loop.
x := 101
loop n in [0..x] or 42 // 42 is the max loop count here
...
TODO
Parallel Loops
Only loops with function bodies (.NET: TPL and PLINQ). The inline loop-body could be compiled into a function.
// what operator? (implies AsParallel)
loop [0..10] ->> LogInt
// (static) partition function
loop [0..10].Partition(3) -> LogInt
loop [0..10].Partition(3) ->> LogInt
// AsParallel function (.NET)
loop [0..10].AsParallel() -> LogInt
Cancelling parallel processing (CancellationToken)?
c = CancellationTokenSource
loop IterObjects().WithCancellation(c.Token) ->> LogInt
loop IterObjects() and c.Token? ->> LogInt
Async Loops
// auto-detect (implicit await) based on enumerator type?
loop n in IterAsync()
...
c = CancellationTokenSource
loop n in IterAsync().WithCancellation(c.Token)
loop n in IterAsync() and c.Token?
...
Cycle<T> Type
An array of values that are cycled through each time it is read.
Cycle<T> is a self-restarting iterator (enumerator).
l := (1, 2, 3, 4, 5)
c := Cycle(l)
c := Cycle(l, 3) // max 3 cycles (?)
m := c // 1
n := c // 2
o := c // 3
p := c // 4
q := c // 5
r := c // 1
s := c // 2
t := c // 3
For use in loops typically.
l := (1, 2, 3, 4, 5)
c := Cycle(l)
loop i in [0..10] or n in c
// i = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
// n = 1, 2, 3, 4, 5, 1, 2, 3, 4, 5
Of course a Cycle can be constructed with a range.
Break
It is possible to break out of the execution of a loop at any time with the break keyword.
Perhaps call it
leaveinstead ofbreakas it sounds less dreadful. Orexit loopandexit ifandexit fn?up(as in up one scope/indent)?
Here’s an example of an endless loop that uses a conditional branch if to break out of the loop:
loop
do_stuff_here
if true
break // this will exit the loop
The break keyword only works on its immediate parent loop. In the case of nested loops, it will look like this:
loop // loop #1
do_stuff_here
loop // loop #2
do_other_stuff_here
if true
break // this will exit loop #2
Let
breakbreak out of a scope in general - not a loop specifically. This would requireifstatement scopes to be ignored?
c :=^ 0
|c| // capture scope
x := c * 42
if x = 0
break
// not executed when x = 0
// back to root scope
c = 42
Continue
The opposite of break, the continue keyword will allow you to skip one iteration of the loop:
loop
if true
continue // this will skip 'do_stuff_here'
do_stuff_here
As with break, continue only works on the immediate parent loop.
Exit
TBD: use
exitas a keyword to replacebreak,continue,yieldandreturn(and abort program)?
exit() // exits program
exit(fn) // exits function (return)
exit(co) // exits coroutine (yield)
exit(iter) // exits current iteration (continue)
exit(loop) // exits loop (break)
exit(scope) // exits current scope (1 up)
exit(err) // throw exception
fn, co, iter etc.. are enum values.
fn: (p: U8): U8
if p == 0 -> exit() // abort program
c: U8
loop n in [0..p]
if n = 42 -> exit(iter) // continue
if n = 101 -> exit(loop) // break
c += 1
exit(fn) c // return c
Downside is that this does not work with nested loops for instance.
use
leaveinstead ofexit? break => escape? continue => skip?
TBD: a way to continue or break a specific outer loop in case of nested loops - or - specifying
exit()with a variable / symbol name that identifies the instance of what to exit
loopFn: (p: U8)
#lp1: loop n in [0..p] // compile time label the loop?
loop i in [0..9]
if n + i = 42 -> exit(n) // continue
if n + i = 101 -> exit(lp1) // break outer
// alternate syntax - no labels required
if n + i = 101 -> exit(loop n) // break outer
if n = 42
exit(loopFn) // return from function
if n = 101
exit(Error("Too large")) // throw exception
TBD: some sort of syntax (operators?) that would allow for ‘conditional arithmetic’ in order to reduce branches as an optimization.
Does the .NET compiler (or runtime) already do branchless optimization?
// instead of
greater: (a: I8, b: I8): I8
if a > b
return a;
else
return b;
// branchless (plain syntax)
greater: (a: I8, b: I8): I8
return a * I8(a > b) + b * I8(b >= a)
// what syntax?
// or let our compiler do an auto tranform?
greater: (a: I8, b: I8): I8
return (a > b) -> a, (b >= a) -> b
// match?
greater: (a: I8, b: I8): I8
return match (a, b)
(a > b) -> a
(b >= a) -> b
_ -> 0 // default case if needed