Variables and Types
Declaring Variables
Section titled “Declaring Variables”lash has two declaration keywords: let for immutable bindings and mut for mutable ones.
let name = "alice"name = "bob" # Error: 'name' is immutable. Declare with 'mut' if you need to reassign.
mut counter = 0counter = counter + 1 # validRedeclaring a variable in the same scope is an error. Use drop to remove it first:
let x = 5let x = 10 # Error: 'x' is already declared in this scope. Use 'drop x' first.
let y = 5drop ylet y = 10 # valid -- 'y' was droppedMutable variables can change type on reassignment:
mut val = 42val = "now a string" # validType System
Section titled “Type System”Variables are dynamically typed. lash supports six core types and one specialized type:
| Type | Example |
|---|---|
string | let greeting = "hello" |
int | let count = 42 |
float | let ratio = 3.14 |
bool | let verbose = true |
list | let files = ["a.txt", "b.txt"] |
object | let config = { editor: "vim", tabs: 4 } |
semver | let ver = semver("1.2.3") |
Arithmetic follows expected promotion rules — mixed int/float operations promote to float. Integer division truncates:
let a = 10 / 3 # 3 (int)let b = 10.0 / 3 # 3.333... (float)let c = 10 % 3 # 1 (modulo)Collection Literals
Section titled “Collection Literals”Lists can hold mixed types:
let ports = [8080, 8443, 9090]let mixed = ["hello", 42, true]Objects use colon syntax for key-value pairs:
let server = { host: "localhost", port: 8080, tls: false }Access object properties with dot or bracket syntax:
server.host # "localhost"server["port"] # 8080
let key = "tls"server[key] # falseRanges produce integer lists:
let digits = 1..10 # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Ranges work anywhere a list is expected, including for loops and method chains.
String Interpolation
Section titled “String Interpolation”Double-quoted strings support four interpolation forms:
let user = "alice"let count = 3
echo "hello $user" # hello aliceecho "${user}_home" # alice_homeecho "files: $(ls | wc -l)" # files: 42echo "total: $((count * 10))" # total: 30Single-quoted strings are literal — no interpolation:
echo 'no $interpolation here' # no $interpolation hereString concatenation uses the ~ operator:
let full = "hello" ~ " " ~ "world"let path = HOME ~ "/documents"Expression Statements
Section titled “Expression Statements”Wrap an expression in parentheses to print its value to stdout:
let x = 42(x) # outputs: 42(x * 2) # outputs: 84([1, 2, 3]) # outputs: [1, 2, 3]Without parentheses, a bare name in statement position is treated as a command.
Scoping
Section titled “Scoping”Variables are lexically scoped to the block where they are declared (function body, if/for/while block, or session-level). Inner scopes can shadow outer variables without modifying them:
let x = "outer"if true { let x = "inner" echo $x # inner}echo $x # outerSubshells inherit a copy of the parent scope. Modifications do not propagate back.
Environment Variables
Section titled “Environment Variables”All let and mut variables are automatically exported to child processes. Objects and lists are serialized as JSON strings when exported.
System environment variables like HOME and PATH are mutable by default. Use the env namespace for explicit access:
echo $HOME # standard interpolationlet editor = env.EDITOR # explicit namespace accessUniversal Properties
Section titled “Universal Properties”Every value has .isSuccess and .isFailure properties:
let result = `grep -r "TODO" src/`if result.isSuccess { echo "found matches"}Every type also defines .min and .max bounds (e.g., int.min, int.max).