Hi Mark! Thank you for your help!
I must have been blind from moving code back and forth and the (display ...) was still from when the output of the macro was simply a symbol, for checking whether the guard expression in the (syntax-case ...) cases works. Also I was confused because the error message mentioned the whole define expression, which is why I looked in the wrong place : ) I got it working now, including the more complex case of having variables in request URLs. (https://gitlab.com/ZelphirKaltstahl/guile-scheme-macros/blob/fccb603a92f2c2d2709e169bbdd516fcf05a6816/procedure-defining/runnable-example.scm) Fortunately I re-read the Guile docs and read about with-syntax. Suddenly with-syntax made a lof of sense :D I think I am finally getting it more or less. There were quite a few snares to overcome for me, but I think that with some trial and error I can now get things done. The complex case should also be moved out of the syntax-case directly and then I can use it in the library and can finally define API routes without much trouble (hopefully! unless I overlooked some possibility for sending requests to the docker API), yay! Regards, Zelphir On 8/5/19 8:51 PM, Mark H Weaver wrote: > Hi Zelphir, > > Zelphir Kaltstahl <zelphirkaltst...@posteo.de> writes: > >> 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. > Right. Before, you were trying to perform the check within a procedural > macro, whose code was run when the 'define-api-route' macro was being > _defined_, which is too early. Here you have the right idea. > >> (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! > No, that's not it. The problem is not with the macro itself, but with > how you're attempting to use it. > > The problem is simply that the macro expands into a definition, and you > are trying to use it within an expression context, where definitions are > not allowed, here: > >> (display >> (define-api-route /containers/json GET >> "application/x-www-form-urlencoded")) > This fails for the same reason that the following fails: > > (display (define foo 1)) > >> While it was possible to have a define form inside a syntax-rules, >> suddenly it seems impossible inside a syntax-case? > For purposes of this issue, it is irrelevant whether the macro was > defined using syntax-rules or syntax-case. > > If you have a macro that expands into a definition, then you can only > use that macro in a context where definitions are allowed. > > Best, > Mark