29 Let Bindings

A let binding gives a name to a path tail so the named result can be used in the expression that follows.

The previous chapter showed that this expression forms products of independently reached values:


x.(v*v as product)

If the intention is to square each value reached by v, bind the reached value once:


x.(let a = v in a*a as product)

The name a refers to the value reached by the binding. The calculation uses that same named value twice.

Naming an intermediate calculation

let can also name an intermediate calculation:


u.course.session.time.(
  let duration = endMinute-startMinute in duration as durationMinutes)

This is deliberately a small example. The calculation is simple enough to write without let, but the shape becomes useful when the intermediate value is used more than once.

Keeping an outer value available

The chapter on this showed that let can preserve an outer value before navigation changes the current value. In the following expression, c names the current course before the path enters its sessions:


u.course.(let c = this in session.(c.code as courseCode, room))

Inside session.(...), the current value is a session. The name c still refers to the outer course.

Gotcha: this changes inside path tails

This expression tries to compare each session with the other sessions for the same course:


u.course.session.(let s=this in
    session.[this ≠ s])

The unqualified inner session is evaluated from the current session, not from the course. Save the course first, then navigate back through the saved course:


u.course.(let c=this in
    session.(let s=this in
        c.session.[this ≠ s]))

Use let outer=this in ... before entering a path tail when the nested expression still needs the outer object.

Let bindings inside filters

A let binding can appear inside a filter condition:


u.course.session.time.[let duration = endMinute-startMinute in duration > 120]

The filter keeps the time slots whose calculated duration is greater than 120.