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.