On 13-02-2023 09:05, Sascha Ziemann wrote: > You also can not ask Scheme about macros, because macros are not > first-class-citizens. > > > This might be interesting: > https://matt.might.net/articles/metacircular-evaluation-and-first-class-run-time-macros/ You actually can ask (Guile)Scheme about macros, and they are first-class (just not procedures, and almost never actually used as first-class things): ;; first-class: macros are just a type of values (let ((the-case-macro (module-ref (current-module) 'case))) (pk the-case-macro) ; -> # ;; This type is disjoint from procedures: (pk (procedure? the-case-macro)) ; -> #false (pk (macro? the-case-macro)) ; -> #true ;; You can use macros to transform syntax to new syntax at runtime: (pk (procedure? (macro-transformer the-case-macro))) ; -> #true (pk ((macro-transformer the-case-macro) #'(case number ((one) 1) ((two) 2)))) ; -> # ;; You can make macros at runtime (though usually you would just ;; pass syntax-transforming procedures instead of the macro wrapper ;; type): (pk (make-syntax-transformer 'pick-a-name 'macro (lambda (s) #'0))) (values)) While unconventional, in principle there is nothing stopping you (besides cross-Scheme compatibility) from using a combination of 'let-syntax-syntax', 'syntax-case' and 'syntax-local-binding' to make let macros accept macros as arguments. Example: (use-modules (system syntax)) (define-syntax call-macro-for-each (lambda (s) (syntax-case s () ((_ macro-identifier arg ...) (call-with-values (lambda () ;; Note: syntax-local-binding implicitly calls macro-transformer (syntax-local-binding #'macro-identifier)) (lambda (type value) (unless (eq? type 'macro) (error "first argument to call-macro-for-each must be an identifier of a macro")) (let loop ((arguments #'(arg ...))) (syntax-case arguments () ((last) (value #'last)) ((stuff . more-stuff) #`(begin #,(value #'stuff) #,(loop #'more-stuff))))))))))) (let-syntax ((pk+quote (lambda (s) #`(pk '#,s '-> #,s)))) (call-macro-for-each pk+quote (+ 1 1) (+ 1 2) (+ 1 3))) ;; Output: ;;; ((+ 1 1) -> 2) ;;; ((+ 1 2) -> 3) ;;; ((+ 1 3) -> 4) $1 = 4 The only snag here is that each macro you want to pass to a 'higher-order macro', you need to give an identifier. As you can always do that with 'let-syntax', that doesn't make it non-first class IMO. Greetings, Maxime.