Search code examples
lambdaschemer5rschicken-scheme

Different treatment of parameters: lambdas vs. defines in Scheme


I have two constructs that I expected to be functionally the same, but they are not and I can't figure out why.

Using define

(define (x2 . b)
  (display b)
  (newline))

(x2 3 4 5) => (3 4 5)

Using lambda

((lambda (. b) 
   (display b) 
   (newline)) 
   3 4 5) => Error: invalid use of `.'

In the R5RS, both definitions and lambdas accept . To make them behave identically I could construct the lambda like this:

((lambda b
   (display b)
   (newline))
   3 4 5) => (3 4 5)

That is one of the valid constructs listed in report's definition of formal parameters. But if I attempt to use it with define, it results in an argument number mismatch error.

(define (x2 b)
  (display b)
  (newline))

(x2 3 4 5) => Error: bad argument count

I thought (define (x y) y) was just syntactic sugar for (define x (lambda (y) y)). It seems like that is true only in most cases. Can anyone explain the rationale for the different behavior?


Solution

  • These expressions represent an anonymous procedure with a fixed arity:

    (define (x y) y)
    (define (x y z) z)
    

    And they're equivalent to:

    (define x (lambda (y) y))
    (define x (lambda (y z) z))
    

    But take this procedure, with a variable number of arguments:

    (define (x . y) y)
    

    It's only equivalent to:

    (define x (lambda y y))
    

    Continuing with the examples, this procedure has a single mandatory parameter and the others are variable:

    (define (x y . z) z)
    

    And its equivalent form would be:

    (define x (lambda (y . z) z))
    

    So, the syntax is a bit different between the implicit/explicit use of a lambda for defining procedures with a variable number of arguments, which is what the dot notation is used for in this case.

    Also, syntax like '( . b) is invalid, because in this case the dot notation is representing an improper list, that is, a list whose last element is not the empty list. On the other hand, this is valid: '(a . b). This explains why the expression (lambda ( . x) x) is invalid; and given that (lambda (x) x) represents an anonymous procedure with a single mandatory parameter, we're left with (lambda x x) for representing an anonymous procedure with only a variable number of arguments and no mandatory arguments.

    As far as I know the only rationale for why things are like this is by convention, and possibly because it's easier to parse.