syntaxschememit-scheme

How is nested variable argument interpreted in Scheme?


The following minimal example is based on this code block:

(define (nested_var_arg . (arg_1 . args))
  (if (list? args)
    (begin
      (displayln "args")
      (displayln args))
    (if (arg_1)
      (displayln arg_1)
      (displayln "nothing"))))

It seems that we can use 0 arg as (define (var_arg . lst) ... ) does. But (nested_var_arg) will throw error ";The procedure #[compound-procedure 12 nested_var_arg] has been called with 0 arguments; it requires at least 1 argument.".

R7RS 4.1.4 Procedures gives one not nestnested_var_arged example (x y . z) and "n or more arguments" seems to imply both var_arg and nested_var_arg accept 0 arg.

Then how to interpret the above (nested_var_arg . (arg_1 . args))?


Solution

  • Then how to interpret the above (nested_var_arg . (arg_1 . args))?

    A pair has a car and a cdr. The list (x y) is a pair with the list (y) in the cdr (which you can verify by evaluating (cdr '(x y)). This is the same as (x . (y)), which is the same as (x . (y . ())). Usually you would write (x y) instead of any of the other forms representing this list, and (x y) is how the Scheme printer will represent any of these forms, but in each case these forms represent the same object: a list containing x and y. Any of these forms is an external representation of a list containing x and y, and the Scheme reader converts external representations into Scheme objects. This is discussed in R7RS 3.3 External representations, R7RS 7.1.2 External representations, and R7RS 6.13.2 Input.

    The form (nested_var_arg . (arg_1 . args)) is an improper list, or a dotted list. The car is nested_var_arg and the cdr is (arg_1 . args). This can be expressed equivalently as (nested_var_arg arg_1 . args); these are external representations of the same improper list. The external representations of proper and improper lists are discussed in R7RS 6.4 Pairs and lists.

    A definition can take one of three forms:

    1. (define <variable> <expression>)
    2. (define (<variable> <formals>) <body>), or
    3. (define (<variable> . <formal>) <body>).

    In the third of these <formal> denotes a single variable, so (define (nested_var_arg arg_1 . args) ;...) matches the second of these. This is described in R7RS 5.3 Variable definitions. Here <formals> corresponds with the formals of the lambda expression (lambda (arg_1 . args) ;...).

    The formals of a lambda expression can take one of three forms:

    1. (<variable1> ...)
    2. <variable>, or
    3. (<variable1> ... <variablen> . <variablen+1>).

    Since (arg_1 . args) matches the third of these, let's focus on that. The triple dot notation indicates that this form requires at least one variable (<variablen>), followed by another variable (<variablen+1>). This notation is described in R7RS 1.3.3 Entry format. The resulting function binds the arguments preceding the dot individually and wraps any remaining arguments in a list bound to <variablen+1>. This means that the function defined using the form (define (nested_var_arg . (arg_1 . args)) ;...) requires at least one argument since this form is a representation of the improper list (define (nested_var_arg arg_1 . args) ;...). Calling the resulting function with no arguments raises an error because it has been defined as requiring at least one argument, i.e., this function expects to bind an argument to arg_1 and will wrap any remaining (optional) arguments in a list bound to args.

    It seems that we can use 0 arg as (define (var_arg . lst) ... ) does.

    This is a different form of definition which matches the third form above: (define (<variable> . <formal>) <body>). Here <formal> corresponds with the formal of the lambda expression (lambda <formal> <body>) (form 2 above where <formal> is <variable>). This form results in a function that takes any number of arguments and wraps them in a list which is bound to <variable>. Calling such a function with no arguments is permissible and results in the empty list being bound to <variable>.

    Is (define (nested_var_arg . (arg_1 . args)) ;...) strictly legal Scheme?

    Scheme requires the read procedure to parse the form (define (nested_var_arg . (arg_1 . args)) ;...) equivalently to (define (nested_var_arg arg_1 . args) ;...), but only this second form is strictly legal syntax for define. Scheme permits an implementation to use read to parse programs, but does not require it. My read of the situation is that implementations are allowed to accept the OP define form, and are likely to do so, but that it is legal for an implementation to reject it.