Booleans
Booleans represent logical truth values: true or false.
Overview
Booleans are fundamental for control flow, conditionals, and logical operations.
Key Characteristics
- Two values - Only
trueorfalse - Logical operators -
&&,||,!(there are noand/or/notkeywords) - Comparison results - All comparisons return booleans
- Short-circuit evaluation - Efficient logical operations
- No truthiness - Other types are not “falsy”;
!nilandnil || "x"are type errors, and a conditionalmatchonly takes an arm that teststrue
When to Use Booleans
Use booleans for:
- Conditional logic (match)
- Flags and toggles
- Validation results
- State tracking (enabled/disabled, active/inactive)
- Filter conditions
Syntax
Boolean Literals
import std:println
# The two boolean values
is_valid = true
is_active = false
# In expressions
user_logged_in = true
debug_mode = false
println(is_valid) # true
println(debug_mode) # false
Logical Operators
AND (&&)
Both operands must be true:
import std:println
println(true && true) # true
println(true && false) # false
println(false && true) # false
println(false && false) # false
# Short-circuit: second operand not evaluated if first is false
result = false && (10 / 0) # No error - second part not evaluated
OR (||)
At least one operand must be true:
import std:println
println(true || true) # true
println(true || false) # true
println(false || true) # true
println(false || false) # false
# Short-circuit: second operand not evaluated if first is true
result = true || (10 / 0) # No error - second part not evaluated
NOT (!)
Negates a boolean value:
import std:println
println(!true) # false
println(!false) # true
println(!!true) # true (double negation)
Comparison Operators
All comparison operators return booleans:
Equality
import std:println
println(5 == 5) # true
println(5 == 6) # false
println(5 != 6) # true
println("hello" == "hello") # true
Relational
import std:println
println(5 < 10) # true
println(5 > 10) # false
println(5 <= 5) # true
println(10 >= 10) # true
String Comparison
import std:println
println("apple" < "banana") # true (lexicographic)
println("zebra" > "aardvark") # true
Compound Conditions
Combining Operators
import std:println
age = 25
has_license = true
# Can drive if adult with license
can_drive = age >= 18 && has_license
println(can_drive) # true
# Student or senior discount
age = 70
is_student = false
gets_discount = is_student || age >= 65
println(gets_discount) # true
Operator Precedence
import std:println
# NOT has highest precedence
println(!false && true) # true (parsed as (!false) && true)
# AND before OR
println(true || false && false) # true (parsed as true || (false && false))
# Use parentheses for clarity
println((true || false) && false) # false
Short-Circuit Evaluation
AND Short-Circuit
import std:println
# Second expression only evaluated if first is true
check_user = |user| {
user != nil && user:is_active
}
# Safe - doesn't error on nil user
println(check_user(nil)) # false
OR Short-Circuit
import std:println
# The right side is only evaluated when the left side is not `true`
is_weekend = |day| day == "sat" || day == "sun"
println(is_weekend("sat")) # true
println(is_weekend("mon")) # false
# Falling back to a default needs a match: `||` will not accept a non-boolean
get_name = |user| {
match user != nil && user:name != nil {
true => user:name,
false => "Guest",
}
}
println(get_name(nil)) # Guest
println(get_name({name: "Alice"})) # Alice
Truthiness
Suji has no truthiness. Only the value true is true, and non-boolean values
are never coerced:
true && exprevaluatesexpr, which must itself be a boolean- anything other than
trueon the left of&&behaves likefalseand short-circuits, without complaining about its type - a non-boolean on the right of
&&/||is a type error once it is reached:true && 5fails with Logical AND requires boolean operands, andnil || "default"fails with Logical OR requires boolean operands !always requires a boolean:!niland!5are type errors
import std:println
# Only boolean `true` counts as true on the left:
println(true && true) # true
println(5 && true) # false
println("hello" && true) # false
# Use comparisons so both sides are really booleans:
text = "hello"
println(text != "" && true) # true
Because || is strict about its right-hand side, the value || fallback idiom
from other languages does not exist in Suji. Use a match or, for maps,
map::get(key, default):
import std:println
settings = {"retries": 3}
# NOT: settings:timeout || 30 (type error, and a missing key raises anyway)
timeout = settings::get("timeout", 30)
println(timeout) # 30
Converting to Boolean
import std:println
# Nil check
value = nil
is_present = value != nil
println(is_present) # false
# Empty check
list = []
is_empty = list::length() == 0
println(is_empty) # true
# Non-empty
text = "hello"
has_text = text::length() > 0
println(has_text) # true
Common Patterns
Validation
import std:println
validate_user = |user| {
has_name = user:name != nil && user:name::length() > 0
has_email = user:email != nil && user:email ~ /^[^@]+@[^@]+$/
is_adult = user:age != nil && user:age >= 18
has_name && has_email && is_adult
}
user = {
name: "Alice",
email: "alice@example.com",
age: 30
}
println(validate_user(user)) # true
Note that user:name raises Key not found if the key is absent, so
user:name != nil only guards against an explicit nil value. To tolerate
missing keys, read them with user::get("name"), which returns nil instead.
Flags and Toggles
import std:println
# Feature flags
config = {
dark_mode: true,
notifications: false,
beta_features: true
}
# Toggle a flag
config:dark_mode = !config:dark_mode
println(config:dark_mode) # false (toggled)
Conditional Assignment
import std:println
# Using match
status = true
message = match status {
true => "Enabled",
false => "Disabled",
}
println(message) # Enabled
# Using ternary-style match (note the comma after the last arm)
is_admin = true
role = match { is_admin => "Administrator", _ => "User", }
println(role) # Administrator
Guard Clauses
import std:println
process_user = |user| {
# Early returns for validation
user == nil && return "Error: No user"
!user:is_active && return "Error: User not active"
user:age < 18 && return "Error: User must be adult"
# Main logic here
"User processed successfully"
}
println(process_user(nil)) # Error: No user
println(process_user({is_active: false, age: 25})) # Error: User not active
Boolean Methods
Type Checking
import std:println
println(true::is_bool()) # true
println(false::is_bool()) # true
println(1::is_bool()) # false
println("true"::is_bool()) # false
Conversion
import std:println
# To string
println(true::to_string()) # true
println(false::to_string()) # false
# Parse from string (simple example)
parse_bool = |s| {
match s {
"true" => true,
"false" => false,
_ => nil,
}
}
println(parse_bool("true")) # true
println(parse_bool("false")) # false
XOR (Exclusive OR)
XOR is true when operands differ:
import std:println
# Manual XOR
xor = |a, b| {
(a || b) && !(a && b)
}
println(xor(true, true)) # false
println(xor(true, false)) # true
println(xor(false, true)) # true
println(xor(false, false)) # false
# Simpler: != for booleans
println(true != true) # false
println(true != false) # true
Common Pitfalls
Pitfall 1: Comparing with True/False
import std:println
is_valid = true
# Unnecessary comparison
println(match { is_valid == true => "yes", _ => "no", }) # yes
# Use the boolean directly
println(match { is_valid => "yes", _ => "no", }) # yes
# Unnecessary comparison
println(match { is_valid == false => "yes", _ => "no", }) # no
# Use negation
println(match { !is_valid => "yes", _ => "no", }) # no
Pitfall 2: Confusing = and ==
import std:println
# Assignment, not comparison
x = 5
x = 10
# Comparison (use this in conditions)
println(match { x == 10 => "ten", _ => "something else", }) # ten
Pitfall 3: Non-Boolean in Conditionals
import std:println
# A conditional match only takes an arm whose test is exactly `true`.
# Any other value simply fails to match:
text = "hello"
println(match { text => "matched", _ => "fell through", }) # fell through
# Explicit check
println(match { text != "" => "matched", _ => "fell through", }) # matched
# Numbers are not truthy either
count = 0
println(match { count => "matched", _ => "fell through", }) # fell through
# Explicit comparison
println(match { count == 0 => "matched", _ => "fell through", }) # matched
Pitfall 4: Operator Precedence
import std:println
# `!` binds tighter than `&&`, so this is (!false) && true
result = !false && true
println(result) # true
# If you meant to negate the whole conjunction, use parentheses
result = !(false && true)
println(result) # true
Examples
All/Any for Lists
Lists have no built-in any() or all(), so write them yourself with a loop and
an early return:
import std:println
all = |list, predicate| {
loop through list with item {
!predicate(item) && return false
}
true
}
any = |list, predicate| {
loop through list with item {
predicate(item) && return true
}
false
}
numbers = [2, 4, 6, 8]
# All even?
all_even = all(numbers, |x| x % 2 == 0)
println(all_even) # true
# Any odd?
any_odd = any(numbers, |x| x % 2 == 1)
println(any_odd) # false
Boolean Algebra
import std:println
# De Morgan's Laws
a = true
b = false
# !(a && b) == !a || !b
println(!(a && b) == (!a || !b)) # true
# !(a || b) == !a && !b
println(!(a || b) == (!a && !b)) # true
State Machine
import std:println
# A map literal with bare identifier keys is parsed as a block when it is the
# whole body of a match arm, so quote the keys here.
traffic_light = |state| {
match state {
"red" => {"running": false, "warning": false},
"yellow" => {"running": true, "warning": true},
"green" => {"running": true, "warning": false},
_ => {"running": false, "warning": false},
}
}
state = traffic_light("yellow")
println("Running: ${state:running}") # true
println("Warning: ${state:warning}") # true
Best Practices
DO:
- Use boolean values directly in conditionals
- Use descriptive names (
is_active,has_permission) - Keep conditions simple and readable
- Use short-circuit evaluation for safety
- Prefer early returns for validation
DON’T:
- Compare booleans to
trueorfalse - Create complex nested conditions
- Use non-descriptive names (
flag,b,x) - Rely on implicit truthiness (Suji requires explicit booleans)
Next Steps
- Learn about Conditional Logic
- Explore Pattern Matching with booleans
- Study Logical Operators in depth
- Check out Relational Operators