Scheme/"The Seasoned Schemer": Question about the syntax of the definition of "try" function

238 Views Asked by At

In their book "The Seasoned Schemer", Felleisen and Friedman introduce the try function. According to http://community.schemewiki.org/?seasoned-schemer, this function can be defined as

(define-syntax try
  (syntax-rules ()
    ((try var a . b)
     (letcc success 
       (letcc var (success a)) . b))))

where letcc is defined as

(define-syntax letcc 
  (syntax-rules () 
    ((letcc var body ...) 
     (call-with-current-continuation 
       (lambda (var)  body ... ))))) 

Now, while I understand what try does and how it can be used, I am having trouble to follow the formal definition of it. What exactly is the meaning of the dot in the application of letcc to success and (letcc var (success a)) . b in the lines

(letcc success 
       (letcc var (success a)) . b)

of try? Or maybe asked differently: Which part of the definition of try establishes that try is evaluated to b if var is called in a?

Edit 1: Sorry, the definition of letcc was incomplete. Added the missing first line.

Edit 2: The following code can be run in Racket.

(define-syntax letcc
   (syntax-rules ()
                 ((letcc var body ...)
                  (call-with-current-continuation
                    (lambda (var)  body ... )))))

 (define-syntax try
   (syntax-rules ()
                 ((try var a . b)
                  (letcc success
                         (letcc var (success a)) . b))))

(try var (+ 1 1) 4)
; output: 2

(try var (var '(2)) 4)
; output: 4
3

There are 3 best solutions below

3
Sylwester On BEST ANSWER

Syntax rules is pattern matching. A dot indicates the car and cdr of a pair just like rest arguments in lambda / define:

(define (my-list . args)
  args)

A list is just nested pairs. eg. (1 2 3) is just fancy way of displaying (1 . (2 . (3 . ()))).

So (this is random symbols in a list) will match (try var a . b) by try matching this, is matches var, random matches a, and (symbols in a list) matches b.

When you see the same in the expansion it means the code should splice the match after the dot. Eg (var . b) with the previous example becomes (is symbols in a list). It is similar to having b ... but cheaper for the system.

0
AudioBubble On

I'm not an expert on the intricacies of Scheme macro syntax, but I think an equivalent definition of try is:

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (letcc success 
       (letcc var (success a)) b ...))))

I find this much easier to read for sure.

(try e <a> <b> <c>) (either version, at least in Racket) then expands to

(letcc success
  (letcc e
    (success <a>))
  <b> <c>)))

So, then, when <a> is being evaluated, e is a continuation which returns its arguments from the inner letcc form, where they're ignored. If e is invoked that's where you end up and then <b> and <c> get evaluated in the normal way (I've only put more than one thing there because I can, and it deals with the whole . ... thing). if e isn't invoked during the evaluation of <a>, then success is invoked, and it's also a continuation which then returns the result of evaluating <a> from the whole form.

At least I think that's what happens.


Below is a chunk of Racket which I used to test I understood things.

(module+ test
  (require rackunit))

(define-syntax let/cc 
  (syntax-rules () 
    ((let/cc var body ...) 
     (call-with-current-continuation 
       (lambda (var) body ... ))))) 

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (let/cc success 
       (let/cc var (success a)) b ...))))

(module+ test
  (check-eqv?
   (try fail (+ 1 1) 4)
   2)

  (check-eqv?
   (try fail (fail '(2)) 4)
   4)

  (check-eqv?
   (try fail
        (begin 1 (fail) (error "failed to fail"))
        4 5 6)
   6))
0
alinsoar On

Let us try and see what happens. I am using mit-scheme.

File try.scm:

(define-syntax letcc
  (syntax-rules ()
     ((letcc var body ...)
      (call-with-current-continuation
          (lambda (var)  body ... )))))

(define-syntax try
  (syntax-rules ()
     ((try var a . b)
      (letcc success
        (letcc var (success a)) . b))))

(try var (+ 1 1) 4)

(try var (var '(2)) 4)

First step: you compile the file:

> (sf "try")

This will generate the try.bin.

Second step, print the desugared syntax:

> (pp (unsyntax (fasload "try")))


;Loading "try.bin"... done
 ................
 (call-with-current-continuation
  (lambda (success)
    (call-with-current-continuation 
        (lambda (var) (success (+ 1 1))))
    4))
 (call-with-current-continuation
  (lambda (success)
    (call-with-current-continuation 
        (lambda (var) (success (var '(2)))))
    4)))

Now you see explicitly what is executed, hence the result.

In the case of (try var (+ 1 1) 4) you jump out 2 nested calcc, as you call success with value 2, while in the (try var (var '(2)) 4) you jump out 1 level and the 4 from the sequence of the 1st continuation will be returned.