Il giorno lun, 28/06/2021 alle 03.15 +0200, Taylan Kammer ha scritto: > On 28.06.2021 01:10, Damien Mattei wrote: > > hi, > > > > i wanted to create a macro that is used like a function definition > > and > > allow return using call/cc: > > > > (define-syntax def > > (syntax-rules (return) > > ((_ (name args ...) body body* ...) > > (define name (lambda (args ...) > > (call/cc (lambda (return) body body* ...))))) > > ((_ name expr) (define name expr)))) > > > > unfortunaly i got error: > > > > scheme@(guile-user)> (def (test x) (cond ((= x 3) 7) ((= x 2) > > (return 5)) > > (else 3))) > > ;;; <stdin>:2:42: warning: possibly unbound variable `return' > > scheme@(guile-user)> (test 2) > > ice-9/boot-9.scm:1685:16: In procedure raise-exception: > > Unbound variable: return > > > > > > any idea? > > Hi Damien, > > This is because of the "hygiene" rule of Scheme, where the notion of > "lexical > scope" is taken very seriously: for an identifier to be bound, it > must be > visible in the lexical (textual) surroundings where it has been > bound. > > So for instance, in the following example code: > > (def (test x) > (cond > ((= x 3) (return 7)) > ((= x 2) (return 5)))) > > We can't see a binding for "return" anywhere in the text, therefore > it cannot > be bound. > > This is good "default" behavior because it makes code more flexible > and easier > to understand. > > An easy way of overcoming this issue is to let the user explicitly > name the > return identifier however they like: > > (define-syntax def > (syntax-rules () > ((_ (name ret arg ...) body body* ...) > (define (name arg ...) > (call/cc (lambda (ret) body body* ...)))))) > > Now you could define: > > (def (test return x) > (cond > ((= x 3) (return 7)) > ((= x 2) (return 5)))) > > Or for instance: > > (def (test blubba x) > (cond > ((= x 3) (blubba 7)) > ((= x 2) (blubba 5)))) > > However, sometimes you're sure that you want to make an implicit > binding for > an identifier, and for those cases you need to write an "unhygienic" > macro > which can be achieved with the more complex macro system "syntax- > case". > > Here's how your desired macro could be defined. I will use > identifiers like > "<foo>" just for easier readability; they don't have any special > meaning: > > (define-syntax def > (lambda (stx) > (syntax-case stx () > ((_ (<name> <arg> ...) <body> <body>* ...) > (let ((ret-id (datum->syntax stx 'return))) > #`(define (<name> <arg> ...) > (call/cc (lambda (#,ret-id) <body> <body>* ...)))))))) > > There's a few things here to take note of: > > - Unlike with syntax-rules, the syntax-case is contained in a lambda > which > takes a single argument: a "syntax object" which is passed to > syntax-case > > - Unlike with syntax-rules, the "body" of the macro (where it begins > with a > 'let') is not immediately part of the generated code; that 'let' is > actually > executed during compile-time. The body of the macro must result in > an > object of the type "syntax object" that represents the generated > code. > > - You see that I define a variable called "ret-id" which I bind to > the result > of the expression: > > (datum->syntax stx 'return) > > which means "create a syntax object in the same lexical environment > as stx, > and is represented by the symbol 'return'." > > - The actual code generation begins within the #`(...) which is a > shorthand > for (quasisyntax (...)) just like '(...) is short for (quote > (...)). The > result of a quasisyntax expression is a syntax object. Basically, > it's the > most convenient way of creating a syntax object, but like syntax- > rules it's > also hygienic by default and you need to insert "unhygienic" syntax > objects > into it explicitly. > > - Within the quasisyntax, I use #,ret-id which is short for (unsyntax > ret-id) > to inject the unhygienic syntax object that holds the symbol > 'return' into > the generated code. > > For someone used to macros in the Common Lisp or Elisp style, this > may seem > over-complicated. It's the cost of the "hygienic by default" > behavior. > > By the way I assume that you're just toying around with the language > to learn. > If you were thinking of using a 'def' macro like this in real code, I > would > discourage it because there's already a built-in mechanism that > allows the > programmer something very similar, called 'let/ec': > > (import (ice-9 control)) > > (define (test x) > (let/ec return > (cond > ((= x 3) (return 7)) > ((= x 2) (return 5)))))
What does this return (defined with let/ec) do ? In the orevious versions I could see the call to call/cc so I could (somewhat) figure out the "jump" imlpied by calling return But in this last case, where is the return behaviour defined ?