Control Flow
BioLang provides familiar control flow constructs — if/else,
for loops, while loops, match expressions, and
try/catch for error handling. A key distinction is that
if/else are expressions that return values,
not just statements.
If / Then / Else
BioLang supports two styles of conditionals: the inline if ... then ... else ...
form and the block form with braces. Both are expressions that return values:
# Inline form with then/else
let x = 42
let label = if x > 50 then "big" else "small"
println(label) # small
# Block form with braces
let score = 85
let grade = if score >= 90 {
"A"
} else if score >= 80 {
"B"
} else if score >= 70 {
"C"
} else {
"F"
}
println(grade) # B
If Without Else
An if without an else branch executes conditionally.
When used as a statement, no else is required:
# Statement usage — no else needed
let verbose = true
if verbose {
println("Debug mode is on")
}
# Inline form
let n = 7
if n > 5 then println(f"{n} is greater than 5")
Conditional Chains
fn classify_gc(gc) {
if gc > 0.6 { "high GC" }
else if gc > 0.4 { "normal GC" }
else if gc > 0.2 { "low GC" }
else { "AT-rich" }
}
let seqs = [dna"GCGCGCGC", dna"ATCGATCG", dna"AAATTTAA"]
for seq in seqs {
let gc = gc_content(seq)
println(f"{seq}: {classify_gc(gc)}")
}
For Loops
The for loop iterates over any iterable value — lists, ranges,
strings, and maps:
# Iterate over a list
let genes = ["BRCA1", "TP53", "EGFR"]
for gene in genes {
if len(gene) <= 4 then println(f"{gene} is short")
else println(f"{gene} is long")
}
# BRCA1 is long
# TP53 is short
# EGFR is short
# Iterate over a range
for i in range(5) {
println(f"Index: {i}")
}
# Iterate with enumerate
let items = ["alpha", "beta", "gamma"]
for pair in enumerate(items) {
println(f"{pair[0]}: {pair[1]}")
}
# 0: alpha
# 1: beta
# 2: gamma
Iterating Over Maps
let gene_scores = {"BRCA1": 0.95, "TP53": 0.87, "EGFR": 0.72}
# Iterate over keys
for gene in keys(gene_scores) {
let score = gene_scores[gene]
if score > 0.8 {
println(f"{gene} is significant (score={score})")
}
}
# Iterate over values
let total = 0.0
for score in values(gene_scores) {
total = total + score
}
println(f"Total: {total}")
While Loops
# Basic while loop
let count = 0
let total = 0
while count < 10 {
total = total + count
count = count + 1
}
println(f"Sum 0..9 = {total}") # Sum 0..9 = 45
# Collatz conjecture
let n = 27
let steps = 0
while n != 1 {
if n % 2 == 0 then n = n / 2
else n = n * 3 + 1
steps = steps + 1
}
println(f"Collatz steps for 27: {steps}") # Collatz steps for 27: 111
Break and Continue
# break exits the loop immediately
let nums = [2, 4, 6, 7, 8, 10]
for n in nums {
if n % 2 != 0 {
println(f"Found odd number: {n}")
break
}
println(f"{n} is even")
}
# 2 is even
# 4 is even
# 6 is even
# Found odd number: 7
# continue skips to the next iteration
let result = []
for n in range(10) {
if n % 3 == 0 { continue }
result = push(result, n)
}
println(result) # [1, 2, 4, 5, 7, 8]
Match Expressions
The match expression selects a branch based on the value being matched.
Use _ as a wildcard to match anything:
# Match on values
fn codon_to_amino(codon) {
match codon {
"ATG" => "Met (Start)",
"TAA" => "Stop",
"TAG" => "Stop",
"TGA" => "Stop",
_ => "other"
}
}
println(codon_to_amino("ATG")) # Met (Start)
println(codon_to_amino("TAA")) # Stop
println(codon_to_amino("GCT")) # other
# Match as an expression
let base = "A"
let complement = match base {
"A" => "T",
"T" => "A",
"G" => "C",
"C" => "G",
_ => "N"
}
println(f"Complement of {base} is {complement}")
Try / Catch
Use try/catch to handle errors gracefully:
# Basic error handling
let result = try {
100 / 0
} catch e {
println(f"Error: {e}")
-1
}
println(f"Result: {result}")
# Try/catch in a pipeline
let values = [10, 0, 5, 0, 2]
let safe_reciprocals = values |> map(|v| {
try { 1.0 / v } catch _ { 0.0 }
})
println(safe_reciprocals) # [0.1, 0, 0.2, 0, 0.5]
Return
Use return for early exit from a function:
fn find_first(items, predicate) {
for item in items {
if predicate(item) {
return item
}
}
return nil
}
let nums = [3, 7, 12, 5, 18, 2]
let first_big = find_first(nums, |x| x > 10)
println(f"First > 10: {first_big}") # First > 10: 12
Nested Loops
# Build a multiplication table
for i in range(1, 4) {
let row = []
for j in range(1, 4) {
row = push(row, i * j)
}
println(f"{i}: {row}")
}
# 1: [1, 2, 3]
# 2: [2, 4, 6]
# 3: [3, 6, 9]
Functional vs. Imperative
BioLang supports both styles. Functional pipes are preferred for transforms; loops are clearer for side effects:
# Functional style (preferred for transforms)
let squares = [1, 2, 3, 4, 5] |> map(|x| x * x)
println(squares) # [1, 4, 9, 16, 25]
let evens = [1, 2, 3, 4, 5, 6] |> filter(|x| x % 2 == 0)
println(evens) # [2, 4, 6]
let total = [1, 2, 3, 4, 5] |> reduce(|a, b| a + b)
println(total) # 15
# Loop style (clearer for side effects and complex logic)
let acc = 0
for val in [1, 2, 3, 4, 5] {
acc = acc + val
}
println(f"Loop total: {acc}") # Loop total: 15
Putting It All Together
# A complete example combining control flow constructs
let sequences = [
{name: "seq1", bases: dna"GCGCGCGC"},
{name: "seq2", bases: dna"ATATATAT"},
{name: "seq3", bases: dna"ATCGATCG"},
{name: "seq4", bases: dna"GGGGCCCC"}
]
for s in sequences {
let gc = gc_content(s.bases)
let category = if gc > 0.6 then "GC-rich"
else if gc < 0.4 then "AT-rich"
else "balanced"
println(f"{s.name}: GC={gc}, {category}")
}
# Filter and summarize
let gc_rich = sequences
|> filter(|s| gc_content(s.bases) > 0.5)
|> map(|s| s.name)
println(f"GC-rich sequences: {gc_rich}")