Thank you!

I am not 100% sure I understood all about the different phases, but I
seem to have semi-understood and am able to use my understanding
combined with a little trial and error.

I've now got it working as follows:

#+BEGIN_SRC racket
#lang racket

(require (for-syntax racket/string))

(define-syntax define-api-route
  (lambda (stx)
    (define (identifier-name->string id)
      (symbol->string (syntax->datum id)))

    (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)
      [(_ route GET my-content-type)
       (string-contains? (identifier-name->string (syntax route)) "abc")
       (syntax (quote aaa))]
      ;; an else branch basically
      [(_ route GET my-content-type) #t
       (syntax (quote bbb))])))
#+END_SRC

However, the actual code is supposed to define some procedures. This
would be quite long all in one
macro. So I want to call another procedure in the cases of syntax-case.
In this other macro, `identifier-name->string` will not be available, so
I want to give the other macro already the route as string, which will
be used to send requests to an API.

#+BEGIN_SRC racket
#lang racket

(require (for-syntax racket/string))

(define-syntax define-api-route
  (lambda (stx)
    (define (identifier-name->string id)
      (symbol->string (syntax->datum id)))

    (define (identifier->symbol id)
      (syntax->datum id))

    (define another-macro
      (lambda (route http-method my-content-type route-as-string)
        (syntax (quote bbb))))

    (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)
      [(_ route GET my-content-type)
       (string-contains? (identifier-name->string (syntax route)) "abc")
       (syntax (quote aaa))]
      ;; an else branch basically
      [(_ route GET my-content-type) #t
       (another-macro (syntax route)
                      'GET
                      (syntax my-content-type)
                      (identifier-name->string (syntax route)))])))
#+END_SRC

This works, but I was hoping to be able to not define everything inside
one macro, but instead split it up into multiple parts. Just imagine how
big that one macro could become. For example something like:

#+BEGIN_SRC racket
#lang racket

(require (for-syntax racket/string))

(define define-simple-api-route
  (lambda (route http-method my-content-type route-as-string)
    (syntax (quote bbb))))

(define-syntax define-api-route
  (lambda (stx)
    (define (identifier-name->string id)
      (symbol->string (syntax->datum id)))

    (define (identifier->symbol id)
      (syntax->datum id))

    (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)
      [(_ route GET my-content-type)
       (string-contains? (identifier-name->string (syntax route)) "abc")
       (syntax (quote aaa))]
      ;; an else branch basically
      [(_ route GET my-content-type) #t
       (define-simple-api-route (syntax route)
                                'GET
                                (syntax my-content-type)

                                (identifier-name->string (syntax
route)))])))
#+END_SRC

But then the procedure `define-simple-api-route` will not be defined for
use in the syntax-case.

You already mentioned the `begin-for-syntax`. However, it is Racket
specific and not available in for example Guile Scheme or other Schemes.
I guess I will have to put things in separate modules then. Or is there
any other way?



On 8/3/19 11:53 AM, Ryan Culpepper wrote:
> On 8/3/19 10:48 AM, Zelphir Kaltstahl wrote:
>> Hi!
>>
>> I am trying to write a macro, which checks the name of an argument
>> for presence a substring. This is not the main purpose of the macro,
>> but I want to do different things depending on the substring being
>> contained or not contained.
>>
>> Here is what I've got so far:
>>
>> ~~~~~
>> ;; A macro to get the identifier name as string, shamelessly copied
>> from StackOverflow and renamed:
>>
>> (define-syntax identifier-name->string
>>    (lambda (stx)
>>      (syntax-case stx ()
>>        ((_ id)
>>         (identifier? #'id)
>>         (datum->syntax #'id (symbol->string (syntax->datum #'id)))))))
>>
>> ;; And the actual macro I want to write:
>>
>> (define-syntax define-api-route
>>    (lambda (stx)
>>      (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE
>> PATH)
>>        [(_ route GET my-content-type)
>>         (string-contains? (identifier-name->string (syntax route))
>> "abc")
>>         (println "abc is in the identifier name")])))
>> ~~~~~
>>
>> With this, Racket will complain, that I am referencing an identifier
>> before its definition:
>>
>> ~~~~~
>>  > (define-syntax identifier-name->string
>>      (lambda (stx)
>>        (syntax-case stx ()
>>          ((_ id)
>>           (identifier? #'id)
>>           (datum->syntax #'id (symbol->string (syntax->datum #'id)))))))
>>  >
>>    (define-syntax define-api-route
>>      (lambda (stx)
>>        (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS
>> TRACE PATH)
>>          [(_ route GET my-content-type)
>>           (string-contains? (identifier-name->string (syntax route))
>> "abc")
>>           (println "abc is in the identifier name")])))
>>  > (define-api-route abc/abc/abc GET
>> application/json)                                                            
>>     
>> ; string-contains?: undefined;
>> ;  cannot reference an identifier before its definition
>> ;   in module: top-level
>> ;   internal name: string-contains?
>> ; [,bt for context]
>
> There are two things going wrong here:
>
> 1. The undefined identifier is `string-contains?`. You are using it in
> the macro body, so you must require the module that provides it
> for-syntax:
>
>   (require (for-syntax racket/string))
>
> The reason you have to do that for `string-contains?` but not for
> `identifier?`, `syntax`, `lambda`, and so on is that the racket
> language implicitly does a `(require (for-syntax racket/base))` for
> you, and all of those other things are provided by racket/base.
> (Technically, racket just provides racket/base for-syntax.)
>
> 2. Your `identifier-name->string` macro needs to be a phase-1 function
> instead.
>
> The implementation of a macro is an expression at a phase 1 higher
> than where the macro itself is defined. The top-level starts at phase
> 0, so the right-hand side of the `define-api-route` is a phase-1
> expression. If you want to *use* (as opposed to *produce syntax
> referring to*) `identifier-name->string` in the macro body, it must
> also be defined at phase 1. Since you want to use it on a phase-1
> *identifier value* and get a phase-1 *string value*, it should be a
> function.
>
> So replace the definition of `identifier-name->string` with this:
>
>   (begin-for-syntax
>     ;; identifier-name->string : Identifier -> String
>     (define (identifier-name->string id)
>       (symbol->string (syntax->datum id))))
>
> If you don't want to use `begin-for-syntax`, there are two other ways
> to define this helper function and make it available to the macro
> body. You can put it in a new module and require that module
> for-syntax (that is, at phase 1). Or you can make the definition local
> to the `define-api-route` macro by moving it just inside that macro's
> `(lambda (stx) ___)`. In both cases, drop the `begin-for-syntax`.
>
> Ryan
>
>
>> ~~~~~
>>
>> If I take away the (syntax ...) in the guard expression however, it
>> will also not work, as template variables may only occur in syntax:
>>
>> ~~~~~
>>  > (define-syntax
>> define-api-route                                                             
>>                            
>> (lambda
>> (stx)                                                                        
>>                                   
>> (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE
>> PATH)                                                  [(_ route GET
>> my-content-type)                                                             
>>                            
>> (string-contains? (identifier-name->string route)
>> "abc")                                                             
>> (println "abc is in the identifier name")])))
>> ; readline-input:19:52: route: pattern variable cannot be used
>> outside of a
>> ;   template
>> ;   in: route
>> ; [,bt for context]
>> ~~~~~
>>
>> I have also tried loads of other stuff, but I cannot find a way to:
>>
>> 1. get the identifier name of route, whatever the user has as route
>> (not a string yet!)
>> 2. check inside the guard expression, whether the identifier name
>> contains a certain substring
>> 3. based on that substring call other macros to do the actual job of
>> defining an API route
>>
>> Can you help me writing this macro?
>> It would also be great, if the macro was portable, meaning that it is
>> usable from any Scheme.
>>
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "Racket Users" group.
>> To unsubscribe from this group and stop receiving emails from it,
>> send an email to racket-users+unsubscr...@googlegroups.com
>> <mailto:racket-users+unsubscr...@googlegroups.com>.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/racket-users/8b706c81-2587-4dc8-aaaa-a900813571f1%40googlegroups.com
>> <https://groups.google.com/d/msgid/racket-users/8b706c81-2587-4dc8-aaaa-a900813571f1%40googlegroups.com?utm_medium=email&utm_source=footer>.
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/71337af0-c4ac-eb07-114d-8ffefe579112%40gmail.com.

Reply via email to