View Source Document

bind-args.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
-->

### `bind-args` ###

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

`bind-args` is a fexpr for evaluating a list of arguments and binding
them to the identifiers in a list of identifiers, as well as asserting
that the correct number of arguments have been given, and that none of
the arguments evaluated to an abort value.

`bind-args` takes a literal list of identifiers, and expresion which
evaluates to a literal list of expressions whose values are to be bound
to those identifiers, an expresion which evaluates to the environment in
which those expressions will be evaluated, and an expression to evaluate
in the new environment in which the identifiers are bound.

    | (bind-args (a b) (literal (1 2)) (env)
    |   (list a b))
    = (1 2)

Expressions in the list of values are evaluated.

    | (bind-args (a b) (literal ((subtract 5 4) (subtract 10 1))) (env)
    |   (list a b))
    = (1 9)

Too many or too few arguments will produce an `illegal-arguments`
abort value.

    | (bind-args (a b) (literal (1)) (env)
    |   (list a b))
    ? abort (illegal-arguments (1))

    | (bind-args (a b) (literal (1 2 3)) (env)
    |   (list a b))
    ? abort (illegal-arguments (1 2 3))

The literal arguments are reported in the abort value.

    | (bind-args (a) (literal ((subtract 5 4) (subtract 1 0))) (env)
    |   a)
    ? abort (illegal-arguments ((subtract 5 4) (subtract 1 0)))

This is how it might be used in a fexpr definition.  The reason for the
seemingly strange requirements of the second and third arguments should
become clear here: typically you would just pass the fexpr's `args` and
`env` to those arguments.

    | (bind add (fexpr (args env)
    |   (bind-args (a b) args env
    |     (subtract a (subtract 0 b))))
    |   (add 4 (add 5 6)))
    = 15

    | (bind add (fexpr (args env)
    |   (bind-args (a b) args env
    |     (subtract a (subtract 0 b))))
    |   (bind r 7
    |     (add r r)))
    = 14

    | (bind add (fexpr (args env)
    |   (bind-args (a b) args env
    |     (subtract a (subtract 0 b))))
    |   (add (subtract 0 0)))
    ? abort (illegal-arguments ((subtract 0 0)))

    | (bind add (fexpr (args env)
    |   (bind-args (a b) args env
    |     (subtract a (subtract 0 b))))
    |   (add 9 9 9))
    ? abort (illegal-arguments (9 9 9))

    | (bind add (fexpr (args env)
    |   (bind-args (a b) args env
    |     (subtract a (subtract 0 b))))
    |   (add 1 n))
    ? abort (unbound-identifier n)

'<<SPEC'

(define bind-args
  (fexpr (args env)
    (let (
      (id-list       (head args))
      (orig-val-list (eval env (head (tail args))))
      (given-env     (eval env (head (tail (tail args)))))
      (expr          (head (tail (tail (tail args)))))
      (bind-args-r   (fexpr (args env)
        (let (
          (self     (eval env (head args)))
          (id-list  (eval env (head (tail args))))
          (val-list (eval env (head (tail (tail args)))))
          (env-acc  (eval env (head (tail (tail (tail args))))))
          )
          (if (equal? id-list ())
            (if (equal? val-list ())
              env-acc
              (abort (list (literal illegal-arguments) orig-val-list)))
            (if (equal? val-list ())
              (abort (list (literal illegal-arguments) orig-val-list))
              (bind actual (eval given-env (head val-list))
                (self self
                  (tail id-list) (tail val-list)
                  (prepend
                    (list (head id-list) actual)
                    env-acc)))))))))
      (bind new-env (bind-args-r bind-args-r id-list orig-val-list env)
        (eval new-env expr)))))