[plt-scheme] one macro in another?
On Aug 10, Jay F Kominek wrote:
> This is roughly what I'm trying to accomplish:
>
> (define-syntax transaction
> (lambda (stx)
> (syntax-case stx ()
> [(_ expr ...)
> #`(let ([final-proc #f])
> #,(define-syntax finally
> (lambda (stx)
> (syntax-case stx ()
> [(_ exp (... ...))
> (syntax (set! final-proc (lambda () exp (... ...))))])))
> (let ([v (begin
> expr ...)])
> (when (procedure? final-proc)
> (final-proc)
> v)))])))
I'm not sure what you're trying to do here, partially because
`finally' is not used in your macro, so I think that you have some
other confusion.
> But you can't stick define-syntax in let. [...]
More specifically, the bove code is trying to define `finally' at the
wrong level -- in the macro body instead of something that is visible
to the user of the macro. (That's also my guess based on what you
explain below.)
> The goal, for what it is worth, is to be able to write something like
> (transaction
> (set-account-balance (+ 10 (read-account-balance)))
> (finally (printf "account balance updated~n")))
> or even
> (transaction
> (let ([new-balance (+ 10 (read-account-balance))])
> (set-account-balance new-balance)
> (finally (printf "new balance is: ~a~n" new-balance))))
> and have some behind-the-scenes logic rerun the transaction body as
> necessary to deal with serialization or deadlock errors, and then at
> the very end just run the finally block.
It looks like your goal is to capture `finally' unhygienicly by the
transaction macro -- a better way to do that is to use syntax
parameters, but in this case I don't think that even that is needed.
As stated above, I don't see a need for anything beyond plain Scheme
code:
(let ([new-balance (+ 10 (read-account-balance))])
(set-account-balance new-balance)
(printf "new balance is: ~a~n" new-balance))
where if `set-account-balance' throws an error, you never get to the
printout. So I'll try to complicate things -- assuming that there is
some `capture-checkpoint' and `restore-checkpoint' functions with the
obvious meanings. In this case, I think that this is close to what
you want:
(let ([cp #f])
(dynamic-wind
(lambda ()
(set! cp (capture-checkpoint)))
(lambda ()
(let ([new-balance (+ 10 (read-account-balance))])
(set-account-balance new-balance)
(set! cp #f) ; no errors => transaction successful
(printf "new balance is: ~a~n" new-balance)))
(lambda ()
(when cp (restore-checkpoint cp)))))
Deriving a macro from this should be easy now.
Alternatively, you might want to have `finally' clauses appear
anywhere in the code, and that's easy to do too, here's a rough
sketch:
(define final-thunks (make-parameter #f))
(define-syntax finally
(syntax-rules ()
[(finally expr ...)
(let ([thunks (final-thunks)])
(if thunks
(final-thunks (cons (lambda () expr ...) thunks))
(error 'finally "cannot be used outside of `transaction'")))]))
(define-syntax transaction
(syntax-rules ()
[(transaction expr ...)
(if (final-thunks)
(error 'transaction "no nested transactions")
(parameterize ([final-thunks '()])
expr ...
;; get here only if there were no errors
(for-each (lambda (t) (t)) (final-thunks))))]))
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!