Search code examples
schemelispsicp

Let as syntactic sugar for lambda


Is the following how a let statement resolves in terms of lambda?

(let((a 4)(b 5)) (+ a b))
-->
((lambda (a b) (+ a b)) 4 5)

Is the general form something along the lines of:

(lambda (key1 key2 ...) <body> (val1 val2 ...))
-->
(let ((key1 val1) (key2 val2) ...) <body>)

What's the advantage of using the let syntax? Is it just the easier readability placing the key/value pair next to each other?


Solution

  • Not only easier readability; it is easier to write code using let than it is to write code that uses lambda to express let. Consider:

    (let ((x 2)
          (y 3))
      (let ((u (+ x y))
            (v (* x y)))
        (display "let version:\n")
        (for-each display (list x ", " y "\n" u ", " v "\n"))))
    
    let version:
    2, 3
    5, 6
    

    And a lambda version of the same construct:

    ((lambda (x y)
       ((lambda (u v)
           (display "lambda version:\n")
           (for-each display (list x ", " y "\n" u ", " v "\n")))
        (+ x y) (* x y))) 2 3)
    
    lambda version:
    2, 3
    5, 6
    

    Both versions work, but the let version expresses intent through use of an appropriate syntax. The lambda version expresses intent only through the semantics of its construction. If you are unaware if the idiom that allows the expression of the idea of let binding, or if you can't piece together what is happening by analyzing the code, the meaning of the lambda version is opaque. But the let version clearly states its intent by using syntax dedicated to let binding.

    Further, as let binding scenarios get more complex, using let offloads a substantial portion of that complexity to the syntax. The above example is a simple nested let, and it is already easy to make mistakes in the lambda version. You have to be sure that the parentheses are placed correctly (I actually managed to get one out of place when writing the example code), and the final version places all of the variable identifiers at the beginning of the construction, with all of the bound values at the end. This means that to understand the lambda version you must keep referencing the beginning and the end to understand what is happening in the body.

    Generally speaking, lambda expresses a very general idea, but let expresses a more specific idea that is often needed. The advantage of let syntax is that it allows you to express the more specific idea easily, concisely, and readably in a way that is less error-prone than always expressing the specific idea by implementing it in terms of the more general idea.