Type System
BioLang is dynamically typed. Every value carries its type at runtime, and you never
write type annotations. Use typeof() to inspect a value's type.
Primitive Types
Int
64-bit signed integers. Underscores can be used as visual separators:
let count = 42
let big = 1_000_000
let negative = -17
println(f"count={count}, big={big}, negative={negative}")
println(f"Type: {typeof(count)}")
Float
64-bit IEEE 754 floating-point numbers:
let pi = 3.14159
let tiny = 1.0e-10
println(f"pi={pi}")
println(f"tiny={tiny}")
println(f"Type: {typeof(pi)}")
Str
UTF-8 strings with f-string interpolation:
let name = "BioLang"
let greeting = f"Hello, {name}!"
println(greeting)
# String operations
println(upper(name))
println(substr(name, 0, 3))
println(split("A,B,C", ","))
println(f"Type: {typeof(name)}")
Bool
let yes = true
let no = false
let result = 5 > 3
let combined = yes && !no
println(f"yes={yes}, no={no}, 5>3={result}, combined={combined}")
println(f"Type: {typeof(yes)}")
Nil
nil represents the absence of a value:
let nothing = nil
println(f"Value: {nothing}")
println(f"Type: {typeof(nothing)}")
# Use ?? for nil coalescing
let safe = nothing ?? "default"
println(f"Safe: {safe}")
Biology Types
DNA
DNA sequences with literal syntax. Valid nucleotides: A, T, C, G, N.
let seq = dna"ATCGATCGATCG"
println(f"Sequence: {seq}")
println(f"Type: {typeof(seq)}")
println(f"Length: {seq_len(seq)} bp")
println(f"GC content: {gc_content(seq)}")
let rc = reverse_complement(seq)
println(f"Reverse complement: {rc}")
let counts = base_counts(seq)
println(f"Base counts: {counts}")
RNA
RNA sequences (A, U, C, G, N). Created via rna"" literals or by
transcribing DNA:
let mrna = rna"AUGCGAUUCGAA"
println(f"RNA: {mrna}")
println(f"Type: {typeof(mrna)}")
let protein = translate(mrna)
println(f"Protein: {protein}")
# Transcribe DNA to RNA
let dna_seq = dna"ATGCGATTCGAA"
let rna_from_dna = transcribe(dna_seq)
println(f"Transcribed: {rna_from_dna}")
Protein
Amino acid sequences using standard one-letter codes:
let prot = protein"MKTLLILAVS"
println(f"Protein: {prot}")
println(f"Type: {typeof(prot)}")
println(f"Length: {seq_len(prot)} aa")
Collection Types
List
Ordered, heterogeneous collections:
let nums = [1, 2, 3, 4, 5]
let genes = ["BRCA1", "TP53", "EGFR"]
println(f"First: {nums[0]}")
println(f"Length: {len(nums)}")
println(f"Type: {typeof(nums)}")
# List operations
let doubled = nums |> map(|x| x * 2)
println(f"Doubled: {doubled}")
let evens = nums |> filter(|x| x % 2 == 0)
println(f"Evens: {evens}")
let total = sum(nums)
println(f"Sum: {total}")
Record
Named key-value pairs (like a dictionary with known keys):
let gene = {name: "BRCA1", chrom: "chr17", start: 43044295}
println(f"Gene: {gene.name}")
println(f"Chrom: {gene.chrom}")
println(f"Type: {typeof(gene)}")
# Records with string keys
let lookup = {"BRCA1": 672, "TP53": 7157}
println(f"BRCA1 ID: {lookup.BRCA1}")
# Spread to create modified copies
let updated = {...gene, score: 0.95, start: 43044300}
println(f"Updated: {updated}")
Table
Column-oriented tabular data with named columns:
let t = table(
gene = ["BRCA1", "TP53", "EGFR"],
score = [0.95, 0.87, 0.72],
chrom = ["chr17", "chr17", "chr7"]
)
println(f"Type: {typeof(t)}")
println(f"Rows: {len(t)}, Cols: {ncols(t)}")
println(t)
Checking Types at Runtime
Use typeof() to get the type name as a string:
println(typeof(42)) # Int
println(typeof(3.14)) # Float
println(typeof("hello")) # Str
println(typeof(true)) # Bool
println(typeof(nil)) # Nil
println(typeof(dna"ATCG")) # DNA
println(typeof(rna"AUGC")) # RNA
println(typeof(protein"MKT")) # Protein
println(typeof([1, 2, 3])) # List
println(typeof({x: 1})) # Record
Type-checking predicates are also available:
println(f"is_int(42): {is_int(42)}")
println(f"is_str(42): {is_str(42)}")
println(f"is_dna(dna\"ATCG\"): {is_dna(dna\"ATCG\")}")
println(f"is_list([1]): {is_list([1])}")
println(f"is_nil(nil): {is_nil(nil)}")
Type Conversions
BioLang provides explicit conversion functions. There is no implicit coercion:
# String to number
let n = int("42")
let f = float("3.14")
println(f"int('42') = {n} ({typeof(n)})")
println(f"float('3.14') = {f} ({typeof(f)})")
# Number to string
let s = str(42)
println(f"str(42) = {s} ({typeof(s)})")
# Number conversions
let as_float = float(42)
let as_int = int(3.7)
println(f"float(42) = {as_float}")
println(f"int(3.7) = {as_int}")
Bio Conversions
Convert strings to biological sequence types with validation:
# String to bio types
let seq = dna("ATCGATCG")
let rna_seq = rna("AUCGAUCG")
let prot = protein("MKT")
println(f"DNA: {seq} ({typeof(seq)})")
println(f"RNA: {rna_seq} ({typeof(rna_seq)})")
println(f"Protein: {prot} ({typeof(prot)})")
# Bio transformations
let dna_seq = dna"ATGATGATG"
let as_rna = transcribe(dna_seq)
let as_protein = translate(as_rna)
println(f"{dna_seq} -> {as_rna} -> {as_protein}")
No Static Typing
BioLang has no static type system, no type annotations on variables or functions, and no generics. Everything is determined at runtime:
# Variables can hold any type
let x = 42
println(f"x = {x} ({typeof(x)})")
x = "now a string"
println(f"x = {x} ({typeof(x)})")
x = dna"ATCG"
println(f"x = {x} ({typeof(x)})")
# Functions accept any argument types
fn describe(val) {
println(f"Value: {val}, Type: {typeof(val)}")
}
describe(42)
describe("hello")
describe([1, 2, 3])
describe(dna"ATCG")