Interactive REPL
The BioLang REPL (Read-Eval-Print Loop) is an interactive environment for
exploratory data analysis, testing code snippets, and learning the language.
Launch it with bl repl. It features syntax highlighting, tab
completion, multi-line editing, persistent history, and a suite of introspection
commands.
Getting Started
# Launch the REPL
bl repl
BioLang v0.1.0 REPL — Type :help for commands, Ctrl+D to exit
>>> let seq = dna"ATCGATCG"
=> dna"ATCGATCG"
>>> seq |> gc_content()
=> 0.5
>>> seq |> reverse_complement()
=> dna"CGATCGAT"
The REPL evaluates each line and prints the result. Variables persist across
lines within a session. The last expression's value is also available as
_ (underscore).
REPL Commands
Commands start with : and provide environment introspection and
session management:
| Command | Description |
|---|---|
| :help | Show all available commands |
| :env | List all variables in the current environment with their types |
| :type <expr> | Show the type of an expression without evaluating it |
| :time <expr> | Evaluate and print execution time |
| :profile <expr> | Run expression with profiling (call counts, hot paths) |
| :load <file> | Load and execute a .bl file into the current environment |
| :save <file> | Save the current session's input history to a .bl file |
| :reset | Clear all variables and reset the environment |
| :plugins | List loaded plugins |
| :clear | Clear the screen |
| :quit | Exit the REPL (also Ctrl+D) |
:env — Environment Inspection
>>> let seq = dna"ATCGATCG"
>>> let gc = seq |> gc_content()
>>> let reads = fastq("sample.fq.gz")
>>> :env
Name Type Value
seq DNA dna"ATCGATCG"
gc Float 0.5
reads Stream Stream<FastqRecord> (unconsumed)
:type — Type Inspection
>>> :type dna"ATCG"
DNA
>>> :type dna"ATCG" |> gc_content()
Float
>>> :type [1, 2, 3] |> map(|x| x * 2)
List<Int>
>>> :type fasta("genome.fa")
Stream<FastaRecord>
:time — Benchmarking
>>> :time dna"ATCGATCG" * 1000000 |> gc_content()
=> 0.5
Time: 12.3ms
>>> :time fastq("big.fq.gz") |> map(|r| r.seq |> gc_content()) |> mean()
=> 0.423
Time: 3.41s
:profile — Profiling
>>> :profile fasta("genome.fa") |> map(|r| r.seq |> kmer_count(21)) |> collect()
Profile Results:
kmer_count called 24x total: 890ms avg: 37.1ms
map called 1x total: 891ms
fasta called 1x total: 45ms
collect called 1x total: 936ms
Total: 936ms
Tab Completion
The REPL provides context-aware tab completion for variables, functions, methods, file paths, and command arguments:
# Variable names
>>> se<TAB>
seq sequence sequences
# Method completion on typed values
>>> seq |> gc_<TAB>
gc_content
# Builtin function completion
>>> ncbi_<TAB>
ncbi_search ncbi_fetch ncbi_gene ncbi_pubmed ncbi_sequence
# File path completion (after quotes)
>>> fasta("data/<TAB>
data/genome.fa data/reads.fa data/reference.fa.gz
# Command completion
>>> :<TAB>
:help :env :type :time :profile :load :save :reset :plugins :clear :quit
Multi-Line Input
The REPL automatically detects incomplete expressions and continues on the next line. Open braces, brackets, and parentheses trigger multi-line mode. You can also explicitly enter multi-line mode:
# Automatic multi-line detection
>>> let results = [1, 2, 3]
... |> map(|x| {
... value: x,
... squared: x * x
... })
... |> to_table()
=> Table(3 rows x 2 cols)
# Pipe chains continue automatically
>>> dna"ATCGATCG"
... |> reverse_complement()
... |> transcribe()
... |> translate()
=> protein"RI"
History
The REPL maintains persistent history across sessions, stored in
~/.biolang/history:
- Up/Down arrows — navigate through previous inputs
- Ctrl+R — reverse search through history
- :save session.bl — save current session inputs to a file
# Reverse search
>>> <Ctrl+R>
(reverse-i-search)`kmer': seq |> kmer_count(21)
# Save your exploration as a script
>>> :save my_analysis.bl
Saved 42 inputs to my_analysis.bl
Rich Output
The REPL formats different value types with appropriate visual representations:
# DNA sequences show with color coding
>>> dna"ATCGATCG"
=> dna"ATCGATCG"
# Tables render with borders and alignment
>>> csv("data.csv") |> head(3)
=> ┌────────┬───────┬──────────┐
│ sample │ gene │ expr │
├────────┼───────┼──────────┤
│ S001 │ BRCA1 │ 12.45 │
│ S002 │ BRCA1 │ 8.91 │
│ S003 │ BRCA1 │ 15.23 │
└────────┴───────┴──────────┘
3 rows x 3 columns
# Maps show key-value pairs
>>> dna"ATCGATCG" |> kmer_count(2)
=> { "AT": 2, "TC": 2, "CG": 2, "GA": 1, "TG": 0, ... }
# Streams show type information
>>> fastq("reads.fq.gz")
=> Stream<FastqRecord> (unconsumed)
Loading Scripts
Use :load to execute a script and bring its definitions into scope.
This is useful for setting up helper functions or loading data:
# Load a utility script
>>> :load utils.bl
Loaded 5 functions, 2 variables from utils.bl
# Now use the loaded functions
>>> my_custom_filter(data)
# Load fresh data
>>> :load setup.bl
>>> :env
Name Type Value
samples Table Table(100 rows x 8 cols)
metadata Table Table(100 rows x 3 cols)
config Map { genome: "hg38", ... }
REPL Tips
-
Use
_to reference the last result:_ |> len() -
Prefix an expression with
;to suppress output:;let data = csv("big_file.csv") -
Use
:timebefore committing to a full analysis to estimate runtime on a subset of data -
Save exploratory sessions with
:saveto convert them into reproducible scripts