define-macro
Marc Nieper-Wißkirchen
(17 Oct 2019 08:52 UTC)
|
Referentially transparent implementation for Guile Lassi Kortela (17 Oct 2019 09:37 UTC)
|
Referentially transparent implementation for Guile Lassi Kortela 17 Oct 2019 09:37 UTC
> several implementations that you provide in the git repository are > based on `define-macro' (or similar constructs). > > The problem with `define-macro' is that it does not preserve > referential transparency so the implementations are somewhat broken. > > For example, the Guile implementation breaks if `lambda*' is being > rebound by a user of the `keyword-lambda' macro. > > A (hopefully) correct implementation of `keyword-lambda' for Guile, > for example, would be: > > (define-syntax keyword-lambda > (lambda (stx) > (syntax-case stx () > ((_ (formals ... (keyword-symbols ...)) . body) > #'(lambda* (formals ... #:key keyword-symbols ...) . body))))) > > (One may even rewrite this into a `syntax-rules' macro, which, > however, isn't possible for the `keyword-call' macro in the Guile > implementation; this has to be implemented in terms of `syntax-case'.) Thank you very much for the close reading! You're right. There were so many implementations that I didn't take the time to find an optimal macro for each, figuring other people could help with that :) Here's a syntax-rules/syntax-case based implementation for Guile. Is this right? ---------------------------------------------------------------------- (use-modules (ice-9 optargs)) (define-syntax keyword-lambda (syntax-rules () ((_ (formals ... (keyword-symbols ...)) body ...) (lambda* (formals ... #:key keyword-symbols ...) body ...)))) (define-syntax keyword-call (lambda (stx) (syntax-case stx () ((_ kw-lambda positional-vals ... (keyword-syms-and-vals ...)) #`(kw-lambda positional-vals ... #,@(let loop ((alls #'(keyword-syms-and-vals ...)) (acc '())) (cond ((null? alls) (reverse acc)) ((null? (cdr alls)) (error "Missing keyword value in keyword-call")) (else (let ((key (symbol->keyword (syntax->datum (car alls)))) (val (cadr alls))) (loop (cddr alls) (cons val (cons key acc)))))))))))) ---------------------------------------------------------------------- Before: > (load "srfi/177-guile.scm") > (let ((lambda* 'mischief)) (let ((foo (keyword-lambda (a b (c d)) (values a b c d)))) (keyword-call foo 1 2 (c 3)))) ;;; <stdin>:2:38: warning: possibly unbound variable `a' ;;; <stdin>:2:38: warning: possibly unbound variable `b' ;;; <stdin>:2:38: warning: possibly unbound variable `c' ;;; <stdin>:2:38: warning: possibly unbound variable `d' ;;; <stdin>:2:66: warning: possibly unbound variable `a' ;;; <stdin>:2:66: warning: possibly unbound variable `b' ;;; <stdin>:2:66: warning: possibly unbound variable `c' ;;; <stdin>:2:66: warning: possibly unbound variable `d' <unnamed port>:2:15: In procedure module-lookup: Unbound variable: a After: > (load "srfi/177-guile.scm") > (let ((lambda* 'mischief)) (let ((foo (keyword-lambda (a b (c d)) (values a b c d)))) (keyword-call foo 1 2 (c 3)))) $1 = 1 $2 = 2 $3 = 3 $4 = #f