Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why define-syntax of or in scheme need consider three conditions?

I'm reading the scheme programming language, in chapter 3, the book use define-syntax to define or and and procedure, and it says the following definition of or is incorrect:

(define-syntax or ; incorrect!
  (syntax-rules ()
    [(_) #f]
    [(_ e1 e2 ...)
     (let ([t e1])
       (if t t (or e2 ...)))]))

And the correct definition is:

(define-syntax or
  (syntax-rules ()
    [(_) #f]
    [(_ e) e]
    [(_ e1 e2 e3 ...)
     (let ([t e1])
       (if t t (or e2 e3 ...)))]))

Why the correct definition need three conditions? I run many tests, the two definitions produce the same results. How can tell me why the first definition is wrong?

like image 514
myy1966 Avatar asked Jun 01 '18 06:06

myy1966


People also ask

What Is syntax Scheme?

Scheme's very simple syntax is based on s-expressions, parenthesized lists in which a prefix operator is followed by its arguments. Scheme programs thus consist of sequences of nested lists.

How do you define a constant in a Scheme?

Just define the value at the toplevel like a regular variable and then don't change it. To help you remember, you can adopt a convention for naming these kinds of constants - I've seen books where toplevel variables are defined with *stars* around their name.

What does cond do in Scheme?

cond works by searching through its arguments in order. It finds the first argument whose first element returns #t when evaluated, and then evaluates and returns the second element of that argument.

What is an expression in Scheme?

A Scheme expression is a construct that returns a value, such as a variable reference, literal, procedure call, or conditional. Expression types are categorized as primitive or derived. Primitive expression types include variables and procedure calls.


1 Answers

Let's consider the hint from the book.

First we define our own version of or:

(define-syntax my-or ; incorrect!
  (syntax-rules ()
    [(_) #f]
    [(_ e1 e2 ...)
     (let ([t e1])
      (if t t (my-or e2 ...)))]))

Then we look at the expression in the hint.

   (letrec ([even?
              (lambda (x)
                (my-or (= x 0)
                       (odd? (- x 1))))]
             [odd?
              (lambda (x)
                (and (not (= x 0))
                     (even? (- x 1))))])      
      (list (even? 20) (odd? 20)))

Let's look at the expansion (I edited the full expansion a little):

(letrec ([even? (lambda (x)
                  (let ([t (= x 0)])
                    (if t t (let ([t (odd? (- x 1))])
                              (if t t #f)))))]
         [odd? (lambda (x) (if (not (= x 0)) (even? (- x 1)) #f))])
  (list (even? 20) (odd? 20)))

The problem here is that the call to odd? in (let ([t (odd? (- x 1))]) ...) is not in tail position. For each loop the let expression will allocate a new variable (on the stack or elsewhere) an eventually we have a memory problem.

In short: The semantics of or is that in (or e1 ... en) the last expression en is in tail position. If we use the simple version of the my-or macro, then

(my-or e1)

expands into

(let ([t e1])
  (if t t #f))]))

and the expression e1 is not in tail position in the output.

like image 124
soegaard Avatar answered Nov 10 '22 11:11

soegaard