Conditional Statements

If / Else

The basic conditional structure in Soul:
score = 85

if (score >= 90) {
    print("Grade: A")
} else if (score >= 80) {
    print("Grade: B")  
} else if (score >= 70) {
    print("Grade: C")
} else {
    print("Grade: F")
}

Truthiness

Soul follows these rules for truthiness:

Falsy Values

  • false
  • null
  • 0
  • "" (empty string)

Truthy Values

  • true
  • Any non-zero number
  • Any non-empty string
  • Lists and Maps (even empty)
  • Functions
// Examples
if (0) { }           // Won't execute
if ("") { }          // Won't execute  
if ([]) { }          // Will execute (empty list is truthy)
if ({}) { }          // Will execute (empty map is truthy)
if ("false") { }     // Will execute (non-empty string)

Ternary Operator

For simple conditions, use the ternary operator:
age = 18
status = age >= 18 ? "adult" : "minor"

// Can be nested (but use sparingly)
grade = score >= 90 ? "A" : score >= 80 ? "B" : "C"

Switch Statements

For multiple conditions on the same value:
day = "Monday"

switch (day) {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        print("Weekday")
        break
    case "Saturday", "Sunday":
        print("Weekend")
        break
    default:
        print("Invalid day")
}
Soul requires explicit break statements to prevent fall-through between cases.

Loops

While Loop

Executes while a condition is true:
count = 0
while (count < 5) {
    print("Count: " + count)
    count = count + 1
}

// Infinite loop with break
while (true) {
    input = console.read()
    if (input == "quit") break
    print("You said: " + input)
}

For Loop

Traditional C-style for loop:
// Basic for loop
for (i = 0; i < 10; i = i + 1) {
    print(i)
}

// Multiple variables
for (i = 0, j = 10; i < j; i = i + 1, j = j - 1) {
    print(i + ", " + j)
}

// Can omit parts
i = 0
for (; i < 5; ) {
    print(i)
    i = i + 1
}

For-In Loop

The most idiomatic way to iterate in Soul:

Iterating Lists

fruits = ["apple", "banana", "orange"]

// Simple iteration
for (fruit in fruits) {
    print(fruit)
}

// With index using range
for (i in 0..fruits.length()-1) {
    print(i + ": " + fruits[i])
}

Iterating Maps

person = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

// Iterate keys only
for (key in person) {
    print(key + ": " + person[key])
}

// Iterate key-value pairs
for (key, value in person) {
    print(key + " = " + value)
}

Range Iteration

// Inclusive range (0 to 4)
for (i in 0..4) {
    print(i)  // 0, 1, 2, 3, 4
}

// Can use variables
start = 5
end = 10
for (n in start..end) {
    print(n)
}

Loop Control

Break

Exit a loop early:
for (i in 0..100) {
    if (i * i > 50) {
        break  // Exit the loop
    }
    print(i)
}

// Breaking nested loops
for (i in 0..3) {
    for (j in 0..3) {
        if (i == j) break  // Only breaks inner loop
        print(i + "," + j)
    }
}

Continue

Skip to the next iteration:
// Print only even numbers
for (i in 0..10) {
    if (i % 2 == 1) {
        continue  // Skip odd numbers
    }
    print(i)
}

// Process valid items only
items = [1, null, 3, null, 5]
for (item in items) {
    if (item == null) continue
    print("Processing: " + item)
}

Error Handling

Try / Catch

Handle errors gracefully:
try {
    // Risky operation
    result = riskyFunction()
    print("Success: " + result)
} catch (error) {
    print("Error occurred: " + error)
}

// Can throw custom errors
soul divide(a, b) {
    if (b == 0) {
        throw("Division by zero!")
    }
    return a / b
}

try {
    result = divide(10, 0)
} catch (e) {
    print("Caught: " + e)  // "Caught: Division by zero!"
}

Finally Block

Execute code regardless of success or failure:
file = null
try {
    file = io.open("data.txt")
    data = file.read()
    process(data)
} catch (e) {
    print("Error: " + e)
} finally {
    // Always runs
    if (file) {
        file.close()
    }
}

Pattern Examples

Early Return Pattern

soul validateUser(user) {
    if (!user) return false
    if (!user.email) return false
    if (!user.age || user.age < 18) return false
    return true
}

Guard Clauses

soul processOrder(order) {
    // Guard clauses first
    if (!order) {
        print("No order provided")
        return
    }
    
    if (order.items.length() == 0) {
        print("Order has no items")
        return
    }
    
    // Main logic here
    processItems(order.items)
}

Loop with Index and Value

items = ["first", "second", "third"]

// Manual index tracking
i = 0
for (item in items) {
    print(i + ": " + item)
    i = i + 1
}

// Or use range
for (i in 0..items.length()-1) {
    print(i + ": " + items[i])
}