Control Flow Expressions
Introduction to Control Flow Expressions in Flowata
In the realm of programming and scripting, control flow is the orchestrator of logic. It determines the sequence in which various parts of your code are executed, allowing you to introduce decision-making, repetition, and more structured pathways into your scripts.
Flowata's Control Flow Expressions are designed to bring this orchestration to your formulas. They empower you to dictate how your formulas respond to different conditions, how they loop through data, and how they manage the overall flow of execution. These expressions are the backbone of dynamic and responsive formulas, enabling you to create more complex and interactive applications.
Whether you're looking to implement simple conditional checks or craft intricate loops, Flowata's Control Flow Expressions provide the tools you need. They are crafted to be intuitive, mirroring the natural flow of logic we use in everyday decision-making. As you navigate this section, you'll gain insights into the various expressions available in Flowata and how to harness them effectively.
From conditional expressions that evaluate "if this, then that" scenarios, to loops that iterate over data sets, Flowata's Control Flow Expressions are designed to make your formulas both powerful and efficient.
Scope Behavior in Control Flow Expressions
Flowata's Control Flow Expressions exhibit a unique behavior when it comes to variable scope. Unlike many typical functions, these expressions inherently access and operate within the local scope of where they are invoked. This means that any variable defined or modified within a Control Flow Expression directly affects its parent scope.
To illustrate this, consider the for
loop expression:
for(["apple", "banana", "cherry"], seq(
setLocal(fruit, $value),
print(fruit)
)); // Output: apple banana cherry
In this example, the fruit
variable is defined and modified within the loop expression. However, its value is directly accessible and modified in the local scope where the loop is invoked. This behavior ensures that developers can naturally manage and manipulate data without the need for explicit data passing or return statements, streamlining the code and making it more intuitive.
This inherent scope behavior is intentional, designed to provide a seamless experience when orchestrating the flow of logic in Flowata. However, developers should be aware of this characteristic to avoid unintended side effects and ensure they understand the scope implications when using Control Flow Expressions.
Conditional Expressions
if(condition1, result1, condition2, result2, ..., elseResult)
Evaluates the conditions one by one and returns the result of the first matching condition. The elseResult
is returned if none of the conditions are true.
setLocal(score, 85);
print(if(lessThan(score, 60), "Fail", lessThan(score, 80), "Pass", "Distinction")); // Output: Pass
Would be be equivalent to writing multiples like this.
setLocal(score, 85);
if(lessThan(score, 60), print("Fail"));
if(lessThan(score, 80), print("Pass"));
if(equals(score, 85), print("Distinction"));
switch(value, case1, result1, case2, result2, ..., defaultResult)
Evaluates the value
and compares it with the case
values in order. If a match is found, the corresponding result
is returned. If no match is found, the defaultResult
is returned. If no defaultResult
is provided, it will default to null
.
setLocal(day, "Tuesday");
print(switch(day, "Monday", "Start of the week", "Tuesday", "Middle of the week", "Friday", "End of the week", "Other day")); // Output: Middle of the week
Loop Expression
for(dataStructure, formula)
Executes the formula for each element in the provided data structure. Depending on the type of data structure, different variables are set:
- For arrays and sets:
$value
is set to the current element. - For objects and maps:
$key
is set to the current key, and$value
is set to the associated value. - For ranges:
$value
is set to the current number in the range.
The appropriate variables are then passed to the formula.
Examples:
- Using an array:
for(["apple", "banana", "cherry"], seq(
setLocal(fruit, $value),
print(fruit)
)); // Output: apple banana cherry
- Using a range:
for(range(6), seq(
setLocal(number, $value),
print(number)
)); // Output: 0 1 2 3 4 5
- Using a range with start, stop, and step:
for(range(2, 10, 2), seq(
setLocal(evenNumber, $value),
print(evenNumber)
)); // Output: 2 4 6 8
- Using an object:
for({ name: "John", age: 30, city: "New York" }, seq(
setLocal(key, $key),
setLocal(value, $value),
print(concat(key, ": ", value))
)); // Output: name: John, age: 30, city: New York
break()
Can only be used within a loop and halts the current loop iteration.
for(range(6), seq(
if(equals($, 3), break()),
print($)
)); // Output: 0 1 2
continue()
Can only be used within a loop and skips the current iteration, proceeding to the next iteration.
for(range(6), seq(
if(equals($, 3), continue()),
print($)
)); // Output: 0 1 2 4 5
Sequential Execution
seq(formula1, formula2, ..., formulaN) or seq([formula1, formula2, ..., formulaN]) or seq({name1: formula1, name2: formula2, ...})
Runs multiple formulas as a group, executing them sequentially. Each formula is evaluated, and their results are returned in an array. The last result can be accessed using the index [-1]
.
Error Handling: If any formula within the sequence errors out, further execution will be stopped and an error object will be thrown.
Error Object Details:
message
: "Sequential execution terminated due to an error."errorType
: "ExecutionError"errorCode
: "Sequential"additionalInfo
:{ "originalError": <ErrorObject>, "name": <Name when an object is used> or "index": <Index when an array is used> }
Example:
setLocal(results, seq(
setLocal(a, 10),
setLocal(b, 20),
print(add(a, b))
));
print(results[-1]); // Output: 30
If a named object is passed, the return type will also be an object with named results.
Example:
setLocal(results, seq({
first: setLocal(a, 10),
second: setLocal(b, 20),
output: print(add(a, b))
}));
print(results.output); // Output: 30
Concurrent Execution
Concurrent execution allows multiple formulas to be run simultaneously, rather than one after the other as in sequential execution. This can be particularly useful when performing operations that might take some time, such as fetching data from multiple sources. By running these operations concurrently, you can potentially save time and make your formulas more efficient.
concurrent(formula1, formula2, ..., formulaN) or concurrent([formula1, formula2, ..., formulaN]) or concurrent({name1: formula1, name2: formula2, ...})
Runs multiple formulas concurrently. Each formula is evaluated, and the results are returned in an array once all formulas have completed their execution.
Error Handling: If any formula within the concurrent group errors out, further execution will be stopped and an error object will be thrown.
Error Object Details:
message
: "Concurrent execution terminated due to an error."errorType
: "ExecutionError"errorCode
: "Concurrent"additionalInfo
:{ "originalError": <ErrorObject>, "name": <Name when an object is used> or "index": <Index when an array is used> }
Example:
Imagine you have two functions, fetchUserData
and fetchProductData
, which retrieve user and product data respectively:
setLocal(data, concurrent(
fetchUserData(),
fetchProductData()
));
print(data[0]); // Output: User data
print(data[1]); // Output: Product data
If a named object is passed, the return type will also be an object with named results.
Example:
setLocal(data, concurrent({
userData: fetchUserData(),
productData: fetchProductData()
}));
print(data.userData); // Output: User data
print(data.productData); // Output: Product data
Timer API
setTimeout(formula, duration, scope="screen")
Schedules a formula to be executed once after a specified duration (in milliseconds). By default, the timer is scoped to the current screen, but you can specify a global scope if needed. Returns a timer ID which can be used to cancel the timer before it fires.
Example:
setLocal(timerId, setTimeout(print("5 seconds passed!"), 5000));
setLocal(globalTimerId, setTimeout(print("This is a global timer"), 5000, "global"));
clearTimeout(timerId, scope="screen")
Cancels a timer set with setTimeout
, preventing the formula from being executed. By default, it clears timers scoped to the current screen, but you can specify a global scope if needed.
Example:
setLocal(timerId, setTimeout(print("This won't be printed"), 5000));
clearTimeout(timerId);
setInterval(formula, duration, scope="screen")
Schedules a formula to be executed repeatedly, with a fixed time delay between each call. By default, the interval is scoped to the current screen, but you can specify a global scope if needed. Returns a timer ID which can be used to cancel the interval.
Example:
setLocal(intervalId, setInterval(print("Another 3 seconds passed!"), 3000));
clearInterval(intervalId, scope="screen")
Cancels a repeated action which was set up using setInterval
. By default, it clears intervals scoped to the current screen, but you can specify a global scope if needed.
Example:
setLocal(intervalId, setInterval(print("This will be printed only once"), 3000));
setTimeout(clearInterval(intervalId, "screen"), 3100);
getCurrentTimers(scope="screen")
Returns an array of active timer IDs for the specified scope. By default, it lists timers scoped to the current screen, but you can specify a global scope if needed. Useful for debugging or for bulk cancellation of timers.
Example:
print(getCurrentTimers()); // Output for screen-scoped timers: [1, 2, 3, ...]
print(getCurrentTimers("global")); // Output for global timers: [4, 5, 6, ...]
Error Handling
tryCatch(tryFormula, catchFormula)
Executes the tryFormula and catches any errors that occur. If an error occurs, the catchFormula is executed provided with the $error
variable that contains the error information limited to the catchFormula
scope.s
tryCatch(
print(divide(10, 0)),
print("Error occurred: " + $error.message);
); // Output: Error occurred: + whatever the message is
throw(errorObject)
Throws an error that halts the execution of the current formula unless caught by a tryCatch
block or similar error-handling mechanism. The errorObject
should be an object created using the error
function, containing details about the error, such as a message, error type, error code, and any additional information that might be relevant.
Example:
throw(error({ message: "An error occurred", errorType: "RuntimeError", errorCode: 101 }));
This can be used in conjunction with tryCatch
for error handling:
tryCatch(
throw(error({ message: "An error occurred", errorType: "RuntimeError", errorCode: 101 })),
print("Error caught: " + $error.message)
); // Output: Error caught: An error occurred
By using the error
function to create standardized error objects, you ensure consistency in error handling and make it easier to debug and handle exceptional cases in your formulas.
Returning Values
In Flowata, the result of a formula is determined by its return value. This return value can be explicitly set using the setReturnValue
function, or it can be implicitly determined by the value of the last executed statement in the formula.
setReturnValue(value)
Sets the return value from the current formula. This function explicitly sets the value to be returned when the formula completes execution.
setReturnValue("foo");
return(value)
Halts the execution of the current formula and returns the provided value. If no value is provided, the language will use the last set return value. If setReturnValue
hasn't been called, the value of the last executed statement in the formula will be returned. If the formula doesn't contain any statements, a default value of null
is returned.
Examples:
- Using
return
without a value after setting a return value:
setReturnValue("bar");
return(); // Returns "bar" and halts execution
- Using
return
without a value and without setting a return value:
add(5, 10);
return(); // Returns 15 (the result of the last executed statement) and halts execution
- Using
return
without any statements in the formula:
return(); // Returns null and halts execution
- Demonstrating the halting behavior of return:
print("This will be printed");
return("Exiting early");
print("This won't be printed"); // This line won't be executed due to the return statement above
By providing both explicit and implicit ways to determine the return value, Flowata offers flexibility in how developers structure and manage their formulas.