[Part 3 of the FScheme series] 

Continuing along in Bill Hails’ book. Be sure to follow the previous posts.


Now we’re going to add the all-powerful ‘lambda’! Since we’ve already done all the environment-passing plumbing from the last post, this will be straight forward. Lambda creates closures (read Bill’s book for an excellent description with pictures).

Essentially, it returns a new function that when called will do the following:

  1. Create a new environment frame, binding parameter to call-time arguments which are evaluated in the caller’s environment
  2. Extend the captured definition-time environment with this new frame
  3. Finally, eval the body in this extended environment

and Lambda env = function
    | [List(parameters); body] –>
        let closure env' args = 
            // bind parameters to actual arguments (evaluated in the caller's environment) 
            let bindings = List.zip parameters args 
            let bind = function Symbol(p), a -> p, (eval env' a)
                              | _ -> failwith
"Malformed 'lambda' parameter." 
            let env'' = List.map bind bindings |> extend env // extend the captured env 
            eval env'' body 
    | _ -> failwith "Malformed Lambda."

When Lambda is called, it’s given the definition-time environment as env. We capture this by way of the inner ‘closure’ function immediately returned as a Function(closure) Expression. Notice that ‘closure’ itself takes the call-time environment as env’ (with a prime mark). Later, when the function happens to be called (potentially in a completely different scope) it is passed the caller’s environment and does it’s magic.

We just need to add this guy to the global environment as a special form and we’re done:

and environment =
    extend [] [
        "*", Function(Multiply)
        "-", Function(Subtract)
        "if", Special(If)
        "let", Special(Let)
        "lambda", Special(Lambda)]

If you want to marvel at the beauty and power of lambda, check out this previous post :-)


As usual, more tests:

    case "(let ((square (lambda (x) (* x x)))) (square 4))" "16" // lambda
    case "(let ((square (lambda (x) (* x x)))) square)" "Function" // print lambda
    case "(let ((times3 (let ((n 3)) (lambda (x) (* n x))))) (times3 4))" "12" // closure
    case "(let ((times3 (let ((makemultiplier (lambda (n) (lambda (x) (* n x))))) (makemultiplier 3)))) (times3 5))" "15" // higher order functions