> I would love to see some library provide better abstractions for
> fairly common cases of keywords like this
Honestly, I don't think I need a library, I need better knowledge. Your
macro works, but I have no idea how I could have come up with it. The
Racket guide gives a good introduction to the language, but when it
comes to the macro system which is basically a language on its own, all
I get is one chapter in the the guide and a huge reference manual. How
did other people learn Racket meta programming? Read the implementation?
I feel like I have opened a box full of electronics parts and all parts
are labeled and documented perfectly, but I have no idea what any of
those labels mean.

It looks like your macro is turning strings into symbols. I changed

  (define-syntax (register-nvim-function! stx)
    (displayln stx)


  (define-syntax (register-nvim-function! stx)
    (displayln (syntax->datum stx))

so that it would print the list-representation of what it got, and all
the strings are symbols:

  > (define-nvim-function (foo bar baz) #:name "_foo" #:eval (void) #:sync 
      (display bar)
      (display baz))
  (register-nvim-function! foo _foo #:eval (void) #:sync hello)

This is not much of an issue since symbols are packed into strings, but
it is a weak spot.

> You would probably then want to add contracts using `expr/c`.
Yes, once I can wrap my head around how the macro works I can harden it.

> I also wonder if you really want to have a mandatory "name" argument
> or if you should infer it based on the identifier being defined
> (perhaps with an option to override). 
I was considering that, but the Racket conventions for function names
(lower-case, using hyphens) are not valid Neovim function names. The
user would either have to use un-Racketey names in Racket code or
specify the name any time, so I might as well make the name mandatory.

> here is how I might write this:

Let me see if I understand how your macro works.

  (define-splicing-syntax-class options
      #:attributes (name [opt 1])
      (pattern (~seq (~alt (~once     (~seq #:name   name:expr))
                           (~optional (~seq #:range range:expr))
                           (~optional (~seq #:eval   eval:expr))
                           (~optional (~seq #:sync  sync?:expr)))
               #:with (opt ...)
               #`(#,@(for*/list ([clause (in-list (list (attr? #:range range)
                                                        (attr? #:eval   eval)
                                                        (attr? #:sync  sync?)))]
                                 #:when clause
                                 [term (in-list clause)])

This is the syntax class of all the keyword-value pairs. My first
mistake was using `~alt`, I should have used `(~seq (~alt ...))` instead
because I accept a sequence of pairs, not just one pair. The syntax
class also matches all the pair at once instead of each class instance
matching only one pair.

The `#:with` directive mathes the `opt ...` pattern against a syntax
object which has been constructed out of all the non-name pairs. But how
does the `name` attribute get its value? Because it's not an `opt`?

  (syntax-parse stx
    [(_ raw-lhs:expr opts:options
        body:expr ...+)

The `raw-lhs` is the thing we want defined. How does it know to match
both `foo` and `(foo bar baz)`?

  #:do [(define-values (ident-stx rhs-stx)
          (normalize-definition #'(define raw-lhs body ...) #'λ #t #t))]

I don't understand why we need this. It takes apart a definition we
built on the fly, takes it apart, but in the template we put thing back
together in the same order:

      (define ident rhs)
      (register-function! ident opts.name opts.opt ...))]))

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.
For more options, visit https://groups.google.com/d/optout.

Reply via email to