Esc
Start typing to search...

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 it

Type 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 it

Type 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 it

This 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 it

Shadowing 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 it

This 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 it

The 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 it

Destructuring

Variables can be bound using pattern matching (destructuring):

Tuple Destructuring

let (x, y) = (10, 20)
x  -- 10
Try it

Record Destructuring

let { name, age } = { name = "Bob", age = 25 }
name  -- "Bob"
Try it

Nested Destructuring

let (x, { a, b }) = (1, { a = 2, b = 3 })
a  -- 2
Try it

Best Practices

  1. Use descriptive names: userName is better than u
  2. Use shadowing for transforms: Chain let x = ... to transform values step by step
  3. Keep scopes small: Define variables close to where they're used
  4. Use type annotations for public APIs and complex expressions
  5. Use destructuring to extract values from tuples and records

Next Steps

Learn about operators to combine values.