Variables & Assignment
BioLang uses let to declare variables. All variables are dynamically typed
— there are no type annotations. Variables can be reassigned freely after
declaration.
Declaring Variables
Use let to bind a value to a name:
let name = "BRCA1"
let count = 42
let ratio = 0.95
let active = true
println(f"Gene: {name}, count: {count}")
println(f"Ratio: {ratio}, active: {active}")
Reassignment
After a variable is declared with let, you can reassign it without
let:
let x = 10
println(f"Before: {x}")
x = 20
println(f"After: {x}")
# Reassignment can change the type
x = "now I'm a string"
println(f"Changed type: {x} (type: {typeof(x)})")
All Value Types
Variables can hold any BioLang value. The type is determined by the value, not a declaration:
# Primitives
let n = 42
let pi = 3.14159
let msg = "hello"
let flag = true
# Bio literals
let seq = dna"ATCGATCG"
let rna_seq = rna"AUCGAUCG"
let prot = protein"MKTLLILAVS"
# Collections
let nums = [1, 2, 3, 4, 5]
let info = {gene: "BRCA1", chrom: "chr17", score: 0.95}
println(f"DNA: {seq} ({typeof(seq)})")
println(f"List: {nums} ({typeof(nums)})")
println(f"Record: {info} ({typeof(info)})")
String Interpolation
Use f"..." strings to embed expressions inside curly braces:
let gene = "TP53"
let pos = 7687550
let score = 0.987
println(f"Gene {gene} at position {pos}")
println(f"Score: {score}, rounded: {int(score * 100)}%")
# Expressions work inside braces
let nums = [10, 20, 30]
println(f"Sum is {sum(nums)}, mean is {mean(nums)}")
Shadowing
You can re-declare a variable with let in the same scope. The new binding
shadows the old one:
let x = 10
println(f"Original: {x}")
let x = x * 2 + 5
println(f"Shadowed: {x}")
# Shadowing can change type
let val = "42"
println(f"String: {val} ({typeof(val)})")
let val = int(val)
println(f"Int: {val} ({typeof(val)})")
Scope Rules
Variables are block-scoped. A variable declared inside {} is not visible
outside:
let outer = "I'm outer"
let result = {
let inner = 100
inner + 50
}
println(f"Result: {result}")
println(f"Outer: {outer}")
# inner is not accessible here
Accumulation Patterns
A common pattern is reassigning a variable inside a loop:
let genes = ["BRCA1", "TP53", "EGFR", "MYC"]
let total = 0
let long_names = []
for g in genes {
total = total + 1
if len(g) >= 4 {
long_names = push(long_names, g)
}
}
println(f"Total: {total}")
println(f"Long names: {long_names}")
Record Spread
The spread operator ... merges records. Later fields override earlier ones:
let defaults = {genome: "GRCh38", min_qual: 30, min_depth: 10}
let config = {...defaults, sample_id: "S001", min_qual: 20}
println(f"Genome: {config.genome}")
println(f"Min qual (overridden): {config.min_qual}")
println(f"Sample: {config.sample_id}")
List Concatenation
Use ++ to concatenate lists or strings:
let a = [1, 2, 3]
let b = [4, 5, 6]
let combined = a ++ b
println(f"Combined: {combined}")
let greeting = "Hello" ++ " " ++ "World"
println(greeting)
Destructuring
BioLang does not support destructuring assignment. To extract values from a record or list, access fields or indices directly:
let info = {gene: "BRCA1", pos: 43044295, score: 0.99}
# Access fields individually
let gene = info.gene
let pos = info.pos
println(f"{gene} at {pos}")
# Access list elements by index
let items = ["first", "second", "third"]
let first = items[0]
println(f"First item: {first}")
No Type Annotations
BioLang is dynamically typed. There are no type annotations on variables, function
parameters, or return types. Use typeof() to inspect types at runtime:
let x = 42
let s = "hello"
let seq = dna"ATCG"
println(f"{x} is {typeof(x)}")
println(f"{s} is {typeof(s)}")
println(f"{seq} is {typeof(seq)}")