KASL
KASL is a statically typed programming language built for audio synthesis and creating audio effects. Its syntax is heavily inspired by Swift, but is not based on it and does not have complex memory management system like ARC.
Example of KASL
For those who want to grasp the syntax, here's an example of KASL code, which includes various features of the language. The syntax of the KASL language can be found at Syntax. For the detailed explanation of every statements, please refer to the Statements.
// Import the std library.
import std
// You can specify the subdirectories just by using "/" in the import statement.
import math/float
// Declare a struct named Thing.
struct Thing {
// Struct fields.
var a = 0
// Static functions, which can be called like "Thing.zero()".
static func zero() -> Int {
return 0
}
// Member functions, in which "self" keyword can be used.
func decrement(amount: Int) {
self.a = self.a - amount
}
}
// Name the any type you want.
typealias Vector2 = [Float; 2]
// Custom operator definitions.
operator infix ** {
precedence: 95,
associativity: right
}
operator prefix ++ {
precedence: 110
}
operator postfix % {
precedence: 110
}
// Custom operator implementations.
func infix **(lhs: Float, rhs: Float) -> Float {
return float.pow(lhs, rhs)
}
func prefix ++(operand: Int) -> Int {
return operand + 1
}
func postfix %(operand: Float) -> Float {
return operand / 100.0
}
// Input variables.
input float_in = 0.0
input int_in = 0
input bool_in = false
input thing_in = Thing()
// Output variables.
output float_out = 0.0
output int_out = 0
output bool_out = false
output thing_out = Thing()
// State variables.
state float_state = 0.0
state int_state = 0
state bool_state = false
state thing_state = Thing()
// Constants.
let pi = 3.1415926535
// The main function.
func main() {
// Static function call.
let zero_thing = Thing.zero()
zero_thing.decrement(-5)
// Function calls.
int_state = add(lhs: zero_thing.a, rhs: 5)
// If and If-else statements.
if bool_in {
int_state = add(lhs: int_state, rhs: 1)
}
if float_in > 0.0 {
float_out = float_in
} else {
float_out = 0.0
}
// Constant loop statement.
var i = 0
loop 4 {
int_state = add(lhs: int_state, rhs: i)
i = i + 1
}
}
// Function with two arguments and a return.
func add(lhs a: Int, rhs b: Int) -> Int {
return a + b
}
Syntax
KASL's syntax is profoundly inspired by Swift. However, there are some original features like input, output and state variables.
Throughout this documentation, you'll learn the basic syntax of KASL.
Scopes
Scopes are the regions of a program where certain variables and functions are accessible.
Global Scope
In KASL, global scope means the top-level scope of the program, which is not enclosed in brackets.
// This is the global scope
struct Foo {
// This is not the global scope
}
func main() {
// This is not the global scope
}
Variables and functions declared in the global scope are accsessible anywhere in the program. They are visible to the external program when the file is being imported.
Local Scope
Local scope is the region of a program that is enclosed in the curly brackets of a function, or curly brackets inside the function body.
func main() {
// This is the scope of main function
{
// This is the scope of the inner block
}
}
Accessibility
Variables declared in a local scope are only accessible within that scope and its nested scopes. They are not visible outside of that scope, whereas variables and functions declared in the global scope are accessible anywhere in the program.
// Declare a global constant
let pi = 3.14
func main() {
// Declare a local variable
let radius = 5.0
{
// This variable is only visible in this block
let diameter = radius * 2
}
// This causes an error because the variable diameter is not accessible here!
// let circumference = diameter * pi
// Calculate the area of a circle
let area = pi * radius * radius
}
Multiple Files
When another KASL program is imported, only the variables and functions declared in the global scope of the imported file are accessible. Variables declared in local scopes of the imported file are not accessible.
For example, we have module.kasl and main.kasl as follows:
module.kasl
// This variable is in the global scope and can be accessed from main.kasl
let pi = 3.14
func calculate_area(radius: Float) -> Float {
// This function is in the global scope and can be accessed from main.kasl
return pi * radius * radius
}
main.kasl
import module
func main() {
let radius = 5.0
// Access the global variable from module.kasl
let area = module.pi * radius * radius
// Call the function from module.kasl
let area2 = module.calculate_area(radius)
}
In this case, pi and calculate_area from module.kasl are accessible in main.kasl because they are declared in the global scope of module.kasl.
Variables
KASL supports variables, which are used to store values that can be used throughout a program. Variables can be assigned values using the = operator, and their values can be accessed by simply using their names.
Every variable in KASL has a type, which determines the kind of data it can hold. The type of a variable is determined at the time of its declaration and cannot be changed later. KASL supports several basic types, including Int, Float, and Bool which are called "Primitive Types". See more about types in the Types section.
KASL has 7 types of variables, which we will cover in the following sections — but don't worry, they are all pretty similar and should be easy for you to understand!
Index
| Item | Description |
|---|---|
| Local Variables | Stores the value in functions. |
| Local Constants | Declares an unchangeable value. |
| Global Input Variables | Receives the input data from the host. |
| Global Output Variables | Passes the output data to the host. |
| Global State Variables | Used to preserve data over execution iterations. |
| Global Constants | Declares an unchangeable value which can be used across the program. |
| Struct Fields | Stores the value in structs. |
1. Local Variables
Local variables are the variables that can be declared and used within a function. They are only accessible within the function they are declared in and cannot be accessed outside of it.
The most basic way to declare a local variable is to simply specify its type and name, and the initial value for the variable. For example:
func main() {
var x: Int = 10
}
In KASL, you can omit the type of a variable, because the compiler automatically infers the type based on the initial value. So the above code can be rewritten as:
func main() {
var x = 10
}
This is much simpler and more concise. We'll be using this style of variable declaration for the variables after this point.
2. Local Constants
Local constants are similar to local variables, but their values cannot be changed once they are assigned. They are declared using the let keyword instead of var. For example:
func main() {
let x = 10
// This will cause an error because x is a constant:
// x = 20
}
3. Global Input Variables
Input variables are special variables that are used to receive input from the execution host. Execution host can be the DAW, CLI, or any other environment that supports KASL. Input variables are declared using the input keyword.
Input variables cannot be assigned values within the program after they are declared.
For example:
input tempo = 0.0
You need to specify the initial value for an input variable for safety reasons, but it'll be overwritten with the actual input value from the execution host when the program is run.
4. Global Output Variables
Just like input variables, output variables are special variables that are used to send output to the execution host. They are declared using the output keyword. For example:
output audio = 0.0
Output variables can be assigned values within the program, and the final result will be sent to the execution host when the program is run.
5. Global State Variables
State variables are special variables that are used to store state information that can be accessed across different iterations.
Because KASL is designed for real-time audio processing, the main function will be called for every single samples, and state variables are used to preserve the value over main function calls.
This is the recommended way to implement things like delay effects, where you need to store the previous samples to calculate the output for the current sample.
They are declared using the state keyword. For example:
state delay_buffer = [0.0; 44100]
6. Global Constants
Global constants are similar to local constants, but they are accessible throughout the entire program. They are declared using the let keyword. For example:
let pi = 3.14
func main() {
// You can access pi here
}
7. Struct Fields
Unlike the previous 6 types of variables, struct fields are the variables that are used to store data within a struct. They are declared within the struct definition using the var keyword, and can be accessed using the dot notation. For example:
struct Point {
var x = 0.0
var y = 0.0
}
When you initialize a struct, the default values for the fields will be used. For example:
func main() {
let p = Point()
// p.x is 0.0
// p.y is 0.0
}
Types
Every value in KASL has a type. The type of a value determines what operations can be performed on it and how much memory it occupies. KASL has three primitive types, arrays, structs, and typealiases.
Primitive Types
KASL has three primitive types: Int, Float, and Bool.
Int is a 32-bit signed integer, Float is a 32-bit floating-point number, and Bool is a boolean value that can be either true or false.
let my_int: Int = 42
let my_float: Float = 3.14
let my_bool: Bool = true
Arrays
KASL supports fixed-size arrays of any type. The size of an array is determined at compile time and cannot be changed at runtime. Arrays are indexed starting from 0.
The type of an array is denoted by the type of its elements and its size, written as [T; N], where T is the type of the elements and N is the size of the array.
For example, an array of 5 integers can be declared as follows:
let my_array: [Int; 5] = [1, 2, 3, 4, 5]
This program declares an array of 5 integers and initializes it with the values 1, 2, 3, 4 and 5.
If you initialize an array with the same value for all elements, you can use the following syntax:
let my_array: [Int; 5] = [0; 5]
KASL supports multi-dimensional arrays as well. For example, a 2D array of floats with 3 rows and 4 columns can be declared as follows:
let my_2d_array: [[Float; 4]; 3] = [
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0]
]
Structs
Structs are user-defined types that can contain multiple fields of different types. Let's say we want to define a struct to represent a point in 2D space. We can define it as follows:
struct Point {
var x = 0.0
var y = 0.0
}
This defines a struct named Point with two fields: x and y, both of type Float.
0.0 is the default value for both fields. When you create a new instance of Point, the fields will be initialized to these default values.
We can create an instance of this struct and access its fields like this:
func main() {
let p = Point()
var x = p.x // x is 0.0
p.x = 5.0 // p.x is set to 5.0
x = p.x // x is now 5.0
}
You cannot define a cyclic struct, meaning a struct cannot contain a field that is of the same type as the struct itself. This is to prevent infinite recursion when trying to create an instance of the struct. For example, the following code will cause a compilation error:
struct Node {
var value = 0
var node = Node() // This will cause a compilation error because it creates a cyclic struct
}
This also happens if the cycle is indirect, for example:
struct A {
var b = B()
}
struct B {
var c = C()
}
struct C {
var a = A()
}
Typealiases
Typealiases allow you to create a new name for an existing type. This can be useful for improving code readability or for creating more descriptive names for complex types.
typealias Age = Int
In this example, we create a typealias named Age that is an alias for the Int type. We can now use Age in our code to refer to Int, which can make our code more readable when we are specifically referring to ages.
func main() {
let my_age: Age = 30
print(my_age) // Output: 30
}
Typealias just creates a new name for an existing type, it does not create a new type. This means that Age and Int are interchangeable in the code.
You can set Int values to Age variables and vice versa without any issues.
Statements
This documentation describes the statements that can be used in KASL.
Declaration Statements
Declaration statements are the statements that can be used in the global scopes.
Global Statements
These are the statements which can be used in the global scope to declare symbols such as variables, structs and functions.
| Keyword | Description | Link |
|---|---|---|
| import | Imports another kasl program. | Import |
| typealias | Names the specific type. | Typealias |
| func | Defines a function. | Function |
| input | Defines an input. | Input |
| output | Defines an output variable. | Output |
| state | Defines a state variable. | State |
| let | Defines a global constant. | Let |
| struct | Defines a struct. | Struct |
| operator infix / prefix / postfix | Defines the precedence and the associativity for the operator. | Operator Definition |
| func infix / prefix / postfix | Declares the implementation of an operator for the specific operand types. | Operator Function |
Struct Statements
Below are the statements can be used inside struct declarations.
| Keyword | Description | Link |
|---|---|---|
| var | Defines a struct field. | Struct Field |
| func | Defines a member function for the struct. | Function |
Statements
Statements can be used inside functions, and creates the actual execution logic.
| Statement | Description | Link |
|---|---|---|
| {} | Creates a scope. | Block |
| var | Declares a local variable. | Var |
| let | Declares a local constant. | Let |
| = | Assigns a value to the variable or its field. | Assign |
| if / if-else | Conditionally execute the statements. | If |
| return | Exits from the function with a return value. | Return |
| loop | Creates a loop that will be executed specific times. | Loop |
Declaration Statements
Declaration statements are the statements that can be used in the global scopes.
Global Statements
These are the statements can be used in the global scope to declare symbols such as variables, structs and functions.
| Keyword | Description | Link |
|---|---|---|
| import | Imports another kasl program. | Import |
| typealias | Names the specific type. | Typealias |
| func | Defines a function. | Function |
| input | Defines an input. | Input |
| output | Defines an output variable. | Output |
| state | Defines a state variable. | State |
| let | Defines a global constant. | Let |
| struct | Defines a struct. | Struct |
| operator infix / prefix / postfix | Defines the precedence and the associativity for the operator. | Operator Definition |
| func infix / prefix / postfix | Declares the implementation of an operator for the specific operand types. | Operator Function |
Struct Statements
Below are the statements can be used inside struct declarations.
| Keyword | Description | Link |
|---|---|---|
| var | Defines a struct field. | Struct Field |
| func | Defines a member function for the struct. | Function |
Import
import statement imports an external KASL file in search paths and makes it accessible inside the program.
Specifications
Symbols in the imported file can be accessed by the file name without the extension .kasl, just like foo.pi in the example below.
If you would like to import a file in the subdirectory of the directory at the search path, you can write the path using / (slash) to access its subdirectories.
Syntax
import <Path to the File>
Example 1
Here's a example of a KASL program, which imports foo.kasl, calls its function and accesses its constant.
foo.kasl
let pi = 3.1415926535
func do_something(value: Float) {
// Do something
}
main.kasl
import foo
func main() {
foo.do_something(foo.pi)
}
Example 2
The following program imports foo/bar.kasl in the search path.
foo/bar.kasl
let e = 2.71828
main.kasl
import foo/bar
output e = 0.0
func main() {
e = bar.e
}
Typealias
typealias statement creates an alias for any type. This is useful for giving a more descriptive name to a complex type, especially arrays.
Syntax
typealias <Alias Name> = <Actual Type>
Example
This example program creates an alias called Vector2.
import std
typealias Vector2 = [Float; 2]
input in_vector = [0.0; 2]
output doubled = [0.0; 2]
func main() {
doubled = double(in_vector)
}
func double(vector: Vector2) -> Vector2 {
return [vector[0] * 2, vector[1] * 2]
}
Function
func statement declares a function in the global scope or struct declarations.
Specifications
Functions can have multiple parameters and single return value. If no return type is specified, the function is expected to return no value.
Every parameter can have an argument label for clarity. If the argument label is specified on function call, the argument will match to be the first parameter that has the same argument label. The subsequent arguments will match to the parameters following the labeled one, so the the order of the parameters cannot be changed. Any parameters skipped in this process will use their default values.
In instance functions (functions in struct statement which are not marked static), self keyword can be used to specify the instance where the function is called.
Static functions can be called on struct name, enabling to create a function which is not called on an instance but belongs to the struct.
Syntax
// Basic
func <Name>() { }
// With parameters
func <Name>(<Param>: <Type>) { }
// With return value
func <Name>(<Param>: <Type>) -> <Type> { }
// With argument label
func <Name>(<Label> <Param>: <Type>) { }
// With default value
func <Name>(<Param> = <Default Value>) { }
// Static function
static func <Name>() { }
Example 1 --- Basic
The program below declares a function called greet, which takes no parameters and returns nothing.
func greet() {
// Do something
}
func main() {
greet()
}
Example 2 --- Parameters and Return Value
The program below declares some functions with parameters and return types, and they can be called as shown in the main function.
import std
func add(lhs: Int, rhs: Int) -> Int {
return lhs + rhs
}
func multiply(times a: Int, by b: Int) -> Int {
return a * b
}
func increment(value: Int = 0) -> Int {
return value + 1
}
// Use the declared functions!
func main() {
add(1, 2) // 3
multiply(times: 3, by: 4) // 12
increment() // 1
increment(10) // 11
}
Example 3 --- Instance Function
The following program creates an instance function called increment and add which mutate the struct field count.
struct Counter {
var count = 0
func increment() {
self.count = self.count + 1
}
func add(amount: Int) {
self.count = self.count + amount
}
}
func main() {
var c = Counter()
c.increment() // 1
c.add(5) // 6
}
Example 4 --- Static Function
The program below creates a static function called unit, which can be called by Circle.unit() using its belonging type name Circle.
struct Circle {
var radius = 0.0
static func unit() -> Circle {
var c = Circle()
c.radius = 1.0
return c
}
}
func main() {
let unit_circle = Circle.unit()
}
Input
input statement declares a new input.
Specifications
Input variable will be given from the execution host, and it is not able to assign a value to the input after declaration.
Like other variable and constant declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
input <Name> = <Default Value>
// With type name
input <Name>: <Type> = <Default Value>
Example
This example program declares an input and uses it to calculate a value.
import std
input in = 0
func main() {
let doubled = in * 2
}
Output
output statement declares a new output variable.
Specifications
The value in the output variables will be sent back to the execution host as an output of the program. If no value is assigned to the output variable after declaration, the default value will be sent to the host.
Like other variable and constant declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
output <Name> = <Default Value>
// With type name
output <Name>: <Type> = <Default Value>
Example
This example program declares a new output variable called out and assigns an integer value 5 to the variable.
output out = 0
func main() {
out = 5
}
State
state statement declares a new state variable.
Specifications
The state variable preserves the containing value over iterations, which is designed to be used for delay, for example.
Like other variable and constant declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
state <Name> = <Default Value>
// With type name
state <Name>: <Type> = <Default Value>
Example
This example program declares a new state variable called counter and increments it in the every iteration.
import std
state counter = 0
func main() {
counter = counter + 1
}
Let
let statement declares a new constant.
Specifications
The constant is a value you cannot change once it is declared.
Like other variable declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
let <Name> = <Default Value>
// With type name
let <Name>: <Type> = <Default Value>
Example
This example program declares a constant called pi, and calculates sine value for pi.
import std
let pi = 3.1415926535
func main() {
let value = std.float.sin(pi) // 0.0
}
Struct
struct statement declares a new struct.
Specifications
A struct is a custom data type that groups related values together as fields. Each field is declared with var and must have a default value.
To create a new instance of a struct, call it like a function with no arguments, such as Foo(). This creates a new instance with all fields set to their default values.
Fields and instance functions can be accessed with ., such as instance.field or instance.method().
Static functions belong to the type itself rather than instance, and can be called as Foo.method().
If you would like to create struct instances with non-default values, define the functions using static just like Foo.new().
For more information about struct fields and member functions, visit the struct field page and function page.
Syntax
struct <Struct Name> {
// Struct Field
var <Field> = <Default Value>
// Instance Function
func <Method>() { }
// Static Function
static func <Method>() { }
}
Example 1 --- Basic
This example program declares a struct called Point and initializes it using Point().
struct Point {
var x = 0.0
var y = 0.0
}
func main() {
var p = Point()
p.x = 3.0
p.y = 4.0
}
Example 2 --- Member Functions
The program below creates a struct called Circle which has a static function to initialize a new circle with the given radius and a instance function that scales the circle by the given factor.
import std
struct Circle {
var radius = 0.0
static func new(radius r: Float) -> Circle {
var c = Circle()
c.radius = r
return c
}
func scale(factor: Float) {
self.radius = self.radius * factor
}
}
func main() {
var c = Circle.new(radius: 5.0)
c.scale(2.0)
}
Operator Definition
operator statement defines a custom operator and its precedence and associativity.
The implementation of the operator is defined separately using the operator function statement.
Specifications
infix operators take two operands on both sides, prefix operators take a single operand on the right, and postfix operators take a single operand on the left.
Precedence determines the order of evaluation when multiple operators appear in an expression. A higher precedence value means the operator binds more tightly.
Associativity determines how operators of the same precedence are grouped.
left groups from the left, right groups from the right, and none disallows chaining operators of the same precedence.
Prefix and postfix operators do not have associativity.
Syntax
operator infix <Symbol> {
precedence: <Precedence>,
associativity: <Associativity>
}
operator prefix <Symbol> {
precedence: <Precedence>
}
operator postfix <Symbol> {
precedence: <Precedence>
}
Example
This example program defines a custom operator ** and declares its implementation for Float using operator function statement.
import std
operator infix ** {
precedence: 95,
associativity: right
}
func infix **(lhs: Float, rhs: Float) -> Float {
return std.float.pow(lhs, rhs)
}
func main() {
let square = 5.0 ** 2.0
}
Operator Function
func statement followed by infix, prefix and postfix is a operator function statement which defines the implementation of operator for the specific operand types.
Specifications
Operator functions must have a return type. Multiple operator functions can be defined for the same symbol with different operand types, allowing operator overloading.
The parameter name does not have to be lhs, rhs or operand, so you can set any parameter name.
Syntax
func infix <Symbol>(lhs: <Type>, rhs: <Type>) -> <Type> { }
func prefix <Symbol>(operand: <Type>) -> <Type> { }
func postfix <Symbol>(operand: <Type>) -> <Type> { }
Example 1 --- Infix Operator
This example program defines a custom infix operator %% that calculates the average of two operands.
import std
operator infix %% {
precedence: 50,
associativity: left
}
func infix %%(lhs: Float, rhs: Float) -> Float {
return (lhs + rhs) / 2.0
}
func infix %%(lhs: Int, rhs: Int) -> Int {
return (lhs + rhs) / 2
}
func main() {
let float_avg = 3.0 %% 7.0 // 5.0
let int_avg = 3 %% 7 // 5
}
Example 2 --- Prefix Operator
The following program defines a custom prefix operator -- that calculates a decremented value.
import std
operator prefix -- {
precedence: 110
}
func prefix --(operand: Int) -> Int {
return operand - 1
}
func main() {
let x = --5
}
Example 3 --- Postfix Operator
This example program defines a custom postfix operator % that converts percentage to ratio.
import std
operator postfix % {
precedence: 110
}
func postfix %(operand: Float) -> Float {
return operand / 100.0
}
func main() {
let ratio = 75.0%
}
Struct Field
var statement in struct declaration is used to declare a field of a struct.
Specifications
Like other variable and constant declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
struct <Struct Name> {
// Without type name
var <Name> = <Default Value>
// With type name
var <Name>: <Type> = <Default Value>
}
Example
This example program declares a struct called Point with fields called x and y.
struct Point {
var x = 0.0
var y = 0.0
}
func main() {
var p = Point()
p.x = 3.0
p.y = 4.0
}
Statements
Statements can be used inside functions, and creates the actual execution logic.
| Statement | Description | Link |
|---|---|---|
| {} | Creates a scope. | Block |
| var | Declares a local variable. | Var |
| let | Declares a local constant. | Let |
| = | Assigns a value to the variable or its field. | Assign |
| if / if-else | Conditionally execute the statements. | If |
| return | Exits from the function with a return value. | Return |
| loop | Creates a loop that will be executed specific times. | Loop |
Block
A block is a sequence of statements enclosed in {}.
Specifications
A block creates a new scope. Variables and constants declared inside a block are not accessible outside of it.
Syntax
{
<Statements>
}
Example
This example program declares two variables named x and y, but y is not accessible outside of the inner scope.
func main() {
let x = 1
{
let y = 2
}
// y is not accessible here.
}
Var
var statement declares a new local variable, which is accessible only inside the scope.
Specifications
The variable is a value you can change multiple times.
Like other variable declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
var <Name> = <Default Value>
// With type name
var <Name>: <Type> = <Default Value>
Example
This example program declares a variable called i for loop count tracking.
import std
func main() {
var i = 0
loop 10 {
do_something(i)
i = i + 1
}
}
func do_something(i: Int) {
// Do something
}
Let
let statement declares a new local constant, which is accessible only inside the scope.
Specifications
The constant is a value you cannot change once it is declared.
Like other variable declarations, it is possible to omit the type name, or you can specify the type of the variable. If the value type is not specified, it will be inferred from the default value.
Syntax
// Without type name
let <Name> = <Default Value>
// With type name
let <Name>: <Type> = <Default Value>
Example
This example program uses constants to break down the calculation.
import std
func main() {
let x = 10
let doubled = x * 2
let result = doubled + 5 // 25
}
Assign
Assign statement assigns a value to a specific variable.
Specifications
input and let declarations cannot be assigned after declaration.
Syntax
<Variable> = <Value>
// Struct field
<Instance>.<Field> = <Value>
Example
This example program assigns a value to a variable and struct fields.
struct Point {
var x = 0.0
var y = 0.0
}
func main() {
var n = 0
n = 5
var p = Point()
p.x = 3.0
p.y = 4.0
}
If
If statement conditionally executes a sequence of statements.
Specifications
The condition does not need parentheses and has to be of Bool type.
Syntax
if <Condition> {
<Statements>
}
if <Condition> {
<Statements>
} else {
<Statements>
}
if <Condition> {
<Statements>
} else if <Condition> {
<Statements>
} else {
<Statements>
}
Example
The following example conditionally executes assign statement.
import std
func main() {
var x = 10
if x > 5 {
x = 0
}
var y = 3
if y > 5 {
y = 1
} else {
y = 0
}
}
Return
return statement exits from the function and returns a value from the function.
Specifications
The return statement must not return a value if the function does not have a return type, and must return a value if the function has a return type specified.
In the function with a return type specified, all execution paths must have return statement that returns a value. For example, the following program will cause an error:
import std
func main() {
do_something(5)
}
func do_something(number: Int) -> Int {
if number > 0 {
return number
}
}
This causes an error because the do_something function will not return a value if number equals zero or is under zero.
Syntax
// No return value
return
// With return value
return <Value>
Example 1 --- No Return Value
The following program conditionally returns and exits from function.
This does not cause an error because the main function does not have return type specified.
import std
func main() {
var x = 10
if x > 100 {
return
}
x = x + 1
}
Example 2 --- With Return Value
The following program declares a function called add that returns an added value.
import std
func main() {
let result = add(3, 5)
}
func add(lhs: Int, rhs: Int) -> Int {
return lhs + rhs
}
Loop
loop statement executes a sequence of statements a specific number of times.
Specifications
The number of iterations must be constant, not a variable.
If you need an index inside the loop, declare a variable and manually increment it.
Syntax
loop <Count> {
<Statements>
}
Example
The following program calculates the sum of the variable i for each iterations.
import std
func main() {
var sum = 0
var i = 0
loop 10 {
sum = sum + i
i = i + 1
}
}