View Source Document

let.robin

;'<<SPEC'

<!--
Copyright (c) 2012-2024, Chris Pressey, Cat's Eye Technologies.
This file is distributed under a 2-clause BSD license.  See LICENSES/ dir.
SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Robin
-->

### `let` ###

    -> Tests for functionality "Evaluate Robin Expression (with Small)"

`let` lets you bind multiple identifiers to multiple values.

An identifier can be bound to a symbol.

    | (let ((a (literal hello))) a)
    = hello

`let` can appear in the binding expression in a `let`.

    | (let ((a (let ((b (literal c))) b))) a)
    = c

`let` can bind a symbol to a fexpr.

    | (let ((a (fexpr (args env)
    |              (let ((x (eval env (head args)))
    |                    (y (eval env (head (tail args)))))
    |                (prepend y x)))))
    |   (a () (literal foo)))
    = (foo)

Bindings established in a `let` remain in effect when evaluating
the arguments things in the body of the `let`.

    | (let ((dup (fexpr (args env)
    |              (bind x (eval env (head args))
    |                (list x x)))))
    |   (dup (dup (literal g))))
    = ((g g) (g g))

Bindings established in a binding in a `let` can be seen in
subsequent bindings in the same `let`.

    | (let ((a (literal hello)) (b (list a))) b)
    = (hello)

Shadowing happens.

    | (let ((a (literal hello))) (let ((a (literal goodbye))) a))
    = goodbye

`let` can have an empty list of bindings.

    | (let () (literal hi))
    = hi

The list of bindings must be a list, or else an abort value will be produced.

    | (let 999 (literal hi))
    ? abort

Each binding in a list must be a list, or else an abort value will be produced.

    | (let (999) (literal hi))
    ? abort

Both the body and the list of bindings are required, or else an abort value
will be produced.

    | (let ()))
    ? abort

    | (let)
    ? abort

Any arguments given beyond the body and list of bindings will be ignored
and discarded, without being evaluated.

    | (let ((a 1)) a foo)
    = 1

Each binding must have at least a name and a value, or else an abort value
will be produced.

    | (let ((a)) a)
    ? abort

    | (let (()) 7)
    ? abort

Anything given in a binding beyond the name and the value will simply be
ignored and discarded, without being evaluated or otherwise examined.

    | (let ((a 1 foo)) a)
    = 1

The identifier in a binding must be a symbol.

    | (let ((3 1)) 3)
    ? abort

`let` is basically equivalent to Scheme's `let*` or Haskell's `let`.

'<<SPEC'


(define let (fexpr (args env)
  (bind body (head (tail args))
    (bind expand-let-r (fexpr (iargs ienv)
      (bind self (eval ienv (head iargs))
        (bind items (eval ienv (head (tail iargs)))
          (if (equal? items ())
            body
            (prepend (literal bind)
              (prepend (head (head items))
                (prepend (head (tail (head items)))
                  (prepend (self self (tail items))
                           ()))))))))
      (eval env (expand-let-r expand-let-r (head args)))))))