Why does this continuation return twice?

Why do I get 2 return values when I run this from the repl?

#lang racket
(require db racket/pretty racket/control)


(define (make-query-func db-path)
   
  (define conn (sqlite3-connect #:database db-path
                                #:mode 'create))
   
  (define sql (get-query))
                             
  (with-handlers ([exn:fail:sql? (lambda (e) (printf "Got exception: ~S~n" e))])
    (let ((result (query-rows conn sql)))
      result)))

(define send-query #f)

(define (get-query)
  (call/comp
   (λ (return-cont)
     (set! send-query return-cont)
     (abort/cc this-prompt (λ () #f)))))

(define this-prompt
  (make-continuation-prompt-tag 'this-prompt))

(call-with-continuation-prompt
 (λ ()
   (make-query-func "this-db.db"))
 this-prompt)

Running:

Welcome to DrRacket, version 9.1 [cs].
Language: racket, with debugging; memory limit: 2048 MB.
#f
> (send-query "SELECT * FROM Foo ORDER BY Col1 DESC")
'(#("one" "two" "three") #("four" "five" "six"))
'(#("one" "two" "three") #("four" "five" "six"))
> (send-query "SELECT COUNT(*) FROM Foo")
'(#(2))
'(#(2))
> 

Thanks in advance.
Neal

When you capture the continuation with (call/comp (λ (return-cont) ....)), you don't delimit it with this-prompt, so the continuation you get looks like this:

(print-result
  (prompt
    (let ([sql [ ]])
      (query-rows conn sql))
    this-prompt)))])

(I've turned the (define sql ....) into a let and simplified away some other distractions.)

The print-result is inserted by the racket language's #%module-begin macro. That's why expressions in the module body get printed out. The print-result form is actually a macro, but for now you can think of it like a procedure that just prints the given value (if not void) and returns it.

(Arguably, the call to print-result should be outside the default module-body prompt, not inside it. Or rather, since the existing prompt is installed by a lower level, maybe print-result should install its own prompt.)

When you apply the continuation, it performs the query, and then the captured call to print-result prints the result. Then it returns the result to the REPL, which prints it again.

#lang racket
(require db racket/pretty racket/control)


(define (make-query-func db-path)
   
  (define conn (sqlite3-connect #:database db-path
                                #:mode 'create))
   
  (define sql (get-query))
                             
  (with-handlers ([exn:fail:sql? (lambda (e) (printf "Got exception: ~S~n" e))])
    (let ((result (query-rows conn sql)))
      result)))

(define send-query #f)

(define (get-query)
  (call/comp
   (λ (return-cont)
     (set! send-query return-cont)
     (abort/cc this-prompt (λ () #f))) this-prompt))

(define this-prompt
  (make-continuation-prompt-tag 'this-prompt))

(call-with-continuation-prompt
 (λ ()
   (make-query-func "this-db.db"))
 this-prompt)

I'm going to think about the sequence of events that you described, but I supplied this-prompt as the second argument to call/comp and that solved the double return. Is there anything else that you see wrong, before I close the book on this?

Thanks a bunch,
Neal

Just to be clear, this use case requires passing the prompt tag to 3 functions: call/comp, abort/cc and call-with-continuation-prompt? Or am I doing something redundant?

Right, all three need the same prompt tag.

1 Like