[1] Using the receive function here is a way to get extract_labels to effectively return two values—labels and insts—without explicitly making a compound data structure to hold them. An alternative implementation, which returns an explicit pair of values, is
function extract_labels(text, receive) { 
    if (is_null(text)) {
        return pair(null, null);

    } else {
        const result = extract_labels(tail(text));
        const insts = head(result);
        const labels = tail(result);
        const next_inst = head(text);

        return is_string(next_inst)
            ? pair(insts, pair(make_label_entry(next_inst, insts), labels))
            : pair(pair(make_instruction(next_inst), insts), labels);
    }
}
which would be called by assemble as follows:
function assemble_alternative(controller_text, machine) {
    const result = extract_labels(controller_text);
    const insts = head(result);
    const labels = tail(result);

    update_insts(insts, labels, machine);

    return insts;
}
You can consider our use of receive as demonstrating an elegant way to return multiple values, or simply an excuse to show off a programming trick. An argument like receive that is the next function to be invoked is called a continuation. Recall that we also used continuations to implement the backtracking control structure in the amb evaluator in section Cound not find label for sec:amb-implementation.
5.2.2 The Assembler