Variables
Variables in Keel are bindings that associate names with values. By default, all bindings are immutable.
Declaring Variables
Use let to declare a variable:
let name = "Alice"
let age = 30
let pi = 3.14159
Try itType Annotations
Keel has strong type inference, but you can add explicit type annotations:
let count: Int = 42
let message: String = "Hello"
let ratio: Float = 0.75
Try itType annotations are optional but can improve code clarity and catch errors earlier.
Immutability
Keel is a pure functional language. All bindings are immutable — once bound, a value cannot be changed:
let x = 10
x = 20 -- Error: Cannot reassign variable 'x'
Try itThis immutability makes code easier to reason about and prevents many common bugs.
Variable Shadowing
To "update" a variable, use variable shadowing by re-declaring it with the same name:
let x = 10
let x = x + 5 -- Creates a new binding, shadows the previous one
x -- 15
Try itShadowing creates a new variable that hides the previous one. Unlike mutation, the shadowed variable can even have a different type:
let x = 1
let x = x + 1 -- x is now 2
let x = "hello" -- x can change type when shadowed
x -- "hello"
Try itThis is useful for transforming values step by step:
let data = getRawData ()
let data = parseData data
let data = validateData data
-- each step shadows the previous binding
Note: Only variables support shadowing. Functions, modules, and type definitions do not allow shadowing — redeclaring them with the same name is an error.
Scope
Variables are block-scoped. A variable is only accessible within the block where it's defined:
let outer = 1
let result =
let inner = 2 -- Only accessible in this block
outer + inner
print inner -- Error: `inner` is not in scope
print result -- Prints 3
Let Expressions
In Keel, let is an expression that introduces bindings:
let area =
let width = 10
let height = 5
width * height
area -- 50
Try itThe value of a let block is the value of its final expression.
Multiple Bindings
You can chain multiple let bindings:
let x = 1
let y = 2
let z = 3
x + y + z -- 6
Try itDestructuring
Variables can be bound using pattern matching (destructuring):
Tuple Destructuring
let (x, y) = (10, 20)
x -- 10
Try itRecord Destructuring
let { name, age } = { name = "Bob", age = 25 }
name -- "Bob"
Try itNested Destructuring
let (x, { a, b }) = (1, { a = 2, b = 3 })
a -- 2
Try itBest Practices
- Use descriptive names:
userNameis better thanu - Use shadowing for transforms: Chain
let x = ...to transform values step by step - Keep scopes small: Define variables close to where they're used
- Use type annotations for public APIs and complex expressions
- Use destructuring to extract values from tuples and records
Next Steps
Learn about operators to combine values.