Trainerflow Docs trainerflow.com
The language › Variables

The language

Variables

Giving values a name with let, changing them later, and where a name can be seen.

Sooner or later a program needs to remember something: the athlete’s starting weight, how many reps they managed last time, which week of the cycle it is. That is what variables are for.

Declaring

A variable is declared with let, followed by its name and, optionally, its initial value:

let sets = 5
let start = 60|kg|
let note = "Keep your back straight"

Several variables can be declared in a single let, separated by commas. Each one can use the variables before it:

let sets = 5, reps = 5, volume = sets * reps

A variable declared without a value starts out as nil:

let best

Names

A name starts with a letter or an underscore, followed by any letters, digits, underscores or question marks. week, day_2, _tmp and made_it? are all fine names. By convention:

  • variables, execs, groups, inputs and outputs are written in snake_case,
  • actions are written in PascalCase, e.g. BackSquat.

Assigning

Once declared, a variable can be given a new value with =:

let weight = 60|kg|
weight = weight + 2.5|kg|

Assigning to a name that was never declared is an error. That is deliberate: it catches misspellings such as wieght = 65|kg| instead of silently creating a new variable.

Collections can be changed one entry at a time, by assigning to a path:

let load = [squat: 60|kg|, bench: 45|kg|]
load.squat = load.squat + 2.5|kg|

Scope

Every block, i.e. every pair of curly braces, opens a new scope. A variable is visible from the point where it is declared until the end of the block that declares it, including any blocks nested inside it.

let total = 0
repeat i:3 {
    let reps = i * 2
    total = total + reps
}
// total is 12 here, but reps is no longer visible

Because total was declared outside the repeat, assigning to it from inside changes the outer variable, and the change survives the loop. reps, on the other hand, only lives inside the block.

The same holds for groups and execs: a group can read and change the variables of the file it is declared in. This is how a program carries its state from one session to the next:

import "coach/strength/squat.cl"

let load = 60|kg|

group session() {
    let s = squat([weight: load, reps: 5], [reps: true], [location: true])
    if (s.reps >= 5) {
        load = load + 2.5|kg|
    }
}

session()
session()

Every call of session reads load, and when the athlete gets all their reps it raises load for the next call.

Shadowing

Declaring a variable with let inside a block, using a name that already exists outside, creates a new variable that hides the outer one until the block ends. The outer variable is left untouched:

let x = 1
if (true) {
    let x = 5  // a different x
}
// x is still 1 here

This is rarely what you want. If you meant to change the outer variable, leave out the let.