2

I do not understand the following behavior between these two Scheme programs:

Program 1:

(define a
  (begin
    (display "hmmm")
    (newline)
    lambda))

This program, run using scheme test.ss, gives me a syntax error at the lambda line without printing out the string "hmm".

Program 2:

(define lambda 5)
(define a (+ 1 2 lambda))

The end result here is that a is equal to 8.


The behavior in the first program is the behavior I expect in both programs. What confuses me is why the second program does not fail with a syntax error. Clearly I am redefining lambda, but I would think that this would fail with a syntax error before that code could actually be run. It seems to me that to know that this is not a syntax error you would need to actually run the program, but if this was the behavior then I would expect the first program to display the string before it errors out.

In short, why does the first program result in a syntax error while the second program does not?

qfwfq
  • 2,416
  • 1
  • 17
  • 30
  • I suspect that the reason is exactly hygenic macros, but I would like some confirmation and I think that this could be useful for future learning schemers – qfwfq Aug 06 '17 at 03:09
  • Redefining `lambda` is perfectly okay; it’s just a name. Using `lambda` bound to its original value without the proper syntax is, well, a syntax error. The key here is what `lambda` is *bound* to, not its name. Scheme has no “keywords”. – Alexis King Aug 06 '17 at 03:10
  • @AlexisKing in that case shouldn't the `display` be executed in the first program before there is any error? – qfwfq Aug 06 '17 at 03:12
  • A syntax error is a compile-time error, not a runtime error. The code never executes anything because it doesn’t even compile. – Alexis King Aug 06 '17 at 03:12
  • @AlexisKing but how does it know that `lambda` is still bound to the original syntax if the code is never run? My confusion is that it seems that in the second program there is some detection of the redefinition of lambda without ever running the code – qfwfq Aug 06 '17 at 03:15
  • 1
    Scheme is an entirely lexically-scoped language, and one of the properties of lexical scoping is that bindings can be completely determined at compile time. `lambda` can essentially be thought of as a macro, which is a compile-time binding, but `define` (in contrast to `define-syntax`) defines a runtime binding. Macros are often intended to be used in all sorts of irregular “shapes”, and misuse raises a syntax error, but runtime values are quite uniform, and they follow the usual Scheme syntax rules. – Alexis King Aug 06 '17 at 03:18
  • @AlexisKing Maybe lexical scoping is done in a more complicated way than I thought then. The way I understood it, all that would be known at compile time in the second program is that the variable `lambda` is free, in which case it would not be any different than the first program and I would expect the same behavior. – qfwfq Aug 06 '17 at 03:28
  • But the variable `lambda` in the second program *isn’t* free. Definition contexts are essentially treated the same as `letrec`, so your second program is equivalent to `(letrec ((lambda 5) (a (+ 1 2 lambda))) (void))`, and `lambda` is quite clearly bound to a runtime value in the context of the `(+ 1 2 lambda)` expression! I’m not sure why you think it is free there. – Alexis King Aug 06 '17 at 03:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151173/discussion-between-jcolemang-and-alexis-king). – qfwfq Aug 06 '17 at 03:34

2 Answers2

1

As Alexis mentioned, it is perfectly fine to redefine lambda.

In your first example, you are calling the procedure define on arguments a, (display "hmmm"), (newline) and lambda. Since Scheme is an eager language it tries to evaluate each argument before the execution, which is most likely why it fails when evaluating lambda (because in this case, lambda is a procedure expecting some argument-id: https://docs.racket-lang.org/guide/lambda.html).

The second example succeeds because when calling define on arguments a and the summation (+ 1 2 lambda), the summation resolves to a tangible type (because the lambda has been redefined as an integer).

Hope that helps.

itsmewiththeface
  • 190
  • 1
  • 4
  • 12
  • Thanks for the attempt, but as I got at in the comments what I didn't understand was how it was known at compile time that the lambda was redefined. The actual evaluation of each piece of code makes sense to me. – qfwfq Aug 06 '17 at 17:48
1

In Scheme lambda and define are top level bindings in the compiler phase. define takes exactly two operands and since you supplied 4 it should react to that first. So lets mend that first:

(define a 
  (begin 
    (display "hmmm")
    (newline)
    lambda)))

Now you get an error about lambda. It's the primitive form to create procedures so the compiler thinks you are using it wrong:

(lambda (x) (+ x x)) ; implementation of double

If you had defined lambda as a variable then the error won't happen since even though this is the way to make procedures, you can make variables with the same name.

(define lambda 10)
(define a 
  (begin (display "hmmm")
  (newline)
  lambda))
; ==> 10 (and it printed "hmmm")

The compiler knows the lexical nature of the code. It knows exactly which bindings are defined and which are to level and in which phase. Top level lambda is no longer available.

Now in your second program you define lambda and then use it, just like im my last example that also works.

Note that in R5RS the compiler assumes redefinitions og library and primitive procedures are compatible and might constant fold:

(define (+ a b)
  (string-append a b))
(display (+ 4 5)) ; displays 9

In it's defence I have violated the R5RS report by making my + incompatible. If it weren't top level it would have been ok:

(let ((+ (lambda (a b) (string-append a b))))
  (+ 4 5)) ; no loinger top level, will signal n error!
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Thanks for catching the error with the `begin`, that doesn't change my question but it was a silly mistake. You mentioned that the compiler knows the lexical nature of the code, which is the part that confused me. Do you have a recommended resource on exactly how this works? – qfwfq Aug 06 '17 at 17:52
  • 1
    @jcolemang If you aren't interested in reading source code or papers on the subject the [R6RS reports part about macros](http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-12.html#node_sec_9.2) explains that keywords (macros) can shadow bindings and vice versa. If you are interested in compilers you might need to look at [Matt Mights material](http://matt.might.net/articles/) or read the source of compilers. Chez were recently rewritten to a [nanopass compiler](https://youtu.be/Os7FE3J-U5Q). – Sylwester Aug 06 '17 at 21:44
  • I'm still pretty new to the world of compilers so the source code is still a little over my head. I'll look into those later, thank you very much! – qfwfq Aug 06 '17 at 21:47