Let Statements

When dealing with more than 2 or 3 values, manipulating them on the stack becomes hard. In these cases, let statements can be helpful. The let keyword allows you to bind stack variables to names. For example, let's say we want to define a function that returns the positive root of a quadratic from its coefficients. It should take \(a\), \(b\) and \(c\) and return

\[\frac{-b + \sqrt{b^2 - 4ac}}{2a}\]

We might do something like this:

import std::prelude::{swap under fourth}

fn sqrt float -> float { 0.5 pow }

fn quad_form float float float -> float {
    under . *
    swap fourth * 4 * - sqrt
    swap neg +
    swap 2 * /
}

But notice that many of the functions are just manipulating the stack (swap, under, . and fourth). If we instead use a let statement, we can bind the function's arguments to a, b and c variables which we can refer to in our expression:

fn quad_form float float float -> float {
    let a b c {
        // 3 stack values are bound. stack is now empty
        // in this block, we can use a, b and c

        b neg
        b b * 4 a c * * -
        sqrt +
        2 a * /
    }
}

This looks much nicer.

fn let Syntax

It's quite common to immediately bind a function's arguments with a let statement. In these cases, you can use the fn let syntax. Here is the quad_form function using fn let:

fn quad_form let a: float b: float c: float -> float {
    b neg
    b b * 4 a c * * -
    sqrt +
    2 a * /
}

let for Syntax

It's also common to bind a for loop's iterator variable with a let statement. In these cases, you can use the let for syntax. Here is a countdown function using let for:

fn main {
    let i for 0 to 10 {
        10 i - putln
    }
}