Hi Guile Users! I made some progress in writing a procedure defining macro for creating procedures which talk to an API.
I now have working code, which checks the name of an identifier in a guard expression: ----8<----8<----8<---- (use-modules (web uri) (web client) (json) (ice-9 iconv) (ice-9 regex)) (define* (send-request-to-docker-socket request-url docker-socket my-content-type #:key (data #f)) (call-with-values (lambda () (http-get request-url #:port docker-socket #:version '(1 . 1) #:keep-alive? #f #:headers `((host . ("localhost" . #f)) (content-type . (my-content-type (charset . "utf-8")))) #:body (scm->json-string data) #:decode-body? #t #:streaming? #f)) (lambda (response response-text) (let ([resp-text-as-string (bytevector->string response-text "utf-8")]) (cons response resp-text-as-string))))) (define-syntax define-api-route (lambda (stx) (define (identifier-name->string id) (symbol->string (syntax->datum id))) ;; Not needed yet. ;; (define (identifier->symbol id) ;; (syntax->datum id)) (define (contains-url-template-variable? route-as-string) (string-match "<[^>]+>" route-as-string)) ;; We do not need a macro to produce syntax. Instead we need to use a procedure to produce the ;; syntax, because we want to use it while evaluating another macro. (define make-simple-api-route-definition-syntax (lambda (route http-method my-content-type route-as-string) (syntax (quote simple-route)))) (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH) [(_ route GET my-content-type) (contains-url-template-variable? (identifier-name->string (syntax route))) (syntax (quote aaa))] ;; an else branch basically [(_ route GET my-content-type) #t (syntax (define* (route docker-socket #:key (data #f)) (call-with-values (lambda () ;; GET request because syntax GET was specified (http-get request-url #:port docker-socket #:version '(1 . 1) #:keep-alive? #f #:headers `((host . ("localhost" . #f)) (content-type . (my-content-type (charset . "utf-8")))) #:body (scm->json-string data) #:decode-body? #t #:streaming? #f)) (lambda (response response-text) (let ([resp-text-as-string (bytevector->string response-text "utf-8")]) (cons response resp-text-as-string))))))]))) (define* (connect-to-docker-socket #:key (socket-path "/var/run/docker.sock")) (let ([docker-sock-addr (make-socket-address AF_UNIX socket-path)] [docker-sock (socket PF_UNIX SOCK_STREAM 0)]) (setsockopt docker-sock SOL_SOCKET SO_REUSEADDR 1) (connect docker-sock docker-sock-addr) docker-sock)) (display (define-api-route /containers/json GET "application/x-www-form-urlencoded")) ;; (let ([dock-sock (connect-to-docker-socket)]) ;; (let ([resp-and-resp-text ;; (/containers/json dock-sock ;; #:data '(("all" . "true") ;; ("filters" . (("name" . #("db")) ;; ("status" . #("running" "exited"))))))]) ;; (display resp-and-resp-text) ;; (newline))) (display (define-api-route /container/json GET "application/x-www-form-urlencoded")) ----8<----8<----8<---- However, it seems, that now I have a new problem. It seems I cannot use a define form inside a syntax-case case! Running the above code I get the following error: ----8<----8<----8<---- ice-9/boot-9.scm:222:17: In procedure map1: Syntax error: /home/user/development/Guile/macros/procedure-defining/runnable-example.scm:78:1: definition in expression context, where definitions are not allowed, in form (define /containers/json (lambda* (docker-socket #:key (data #f)) (call-with-values (lambda () (http-get request-url #:port docker-socket #:version (quote (1 . 1)) #:keep-alive? #f #:headers (quasiquote ((host "localhost" . #f) (content-type "application/x-www-form-urlencoded" (charset . "utf-8")))) #:body (scm->json-string data) #:decode-body? #t #:streaming? #f)) (lambda (response response-text) (let ((resp-text-as-string (bytevector->string response-text "utf-8"))) (cons response resp-text-as-string)))))) ----8<----8<----8<---- While it was possible to have a define form inside a syntax-rules, suddenly it seems impossible inside a syntax-case? Removing the (syntax ...) around the define form does not help either and gives the following error: ----8<----8<----8<---- ice-9/psyntax.scm:2612:57: In procedure build-dispatch-call: Syntax error: /home/user/development/Guile/macros/procedure-defining/runnable-example.scm:50:7: definition in expression context, where definitions are not allowed, in form (define route (lambda* (docker-socket #:key (data #f)) (call-with-values (lambda () (http-get request-url #:port docker-socket #:version (quote (1 . 1)) #:keep-alive? #f #:headers (quasiquote ((host "localhost" . #f) (content-type my-content-type (charset . "utf-8")))) #:body (scm->json-string data) #:decode-body? #t #:streaming? #f)) (lambda (response response-text) (let ((resp-text-as-string (bytevector->string response-text "utf-8"))) (cons response resp-text-as-string)))))) ----8<----8<----8<---- Once I exchange the define form inside the syntax-case with something like (syntax (quote abc)), the error disappears, but then I am not reaching the goal of defining a procedure. I don't know how to make progress and it is not like searching "procedure defining macros" yields particularly useful results and examples of syntax-case usage I could find simply only use one case and are not helpful for my purpose. I could not even find information about whether there is an "else" keyword for syntax-case and decided to simply use #t for the guard expression. If anyone can point me to an easy to understand resource, which collects all this information and what to do how with macros, that would awesome. I started reading the syntax-case paper by Dybvig, but really, it should not be necessary to read all of it, in order to understand how to use macros. I still seem to have gaps in my understanding, otherwise I would be able to simply write this macro down and be done. Currently I am not sure where I can find useful examples of comparable macros, that show me how these things are usually done. What can I do, how can I change my code to get my procedure defined inside syntax-case, or better even call another procedure, which give me back the syntax, which will be the result of that syntax-case case? Regards, Zelphir