1
votes

I am using the macro mentioned on this page (Macro for keyword and default values of function arguments in Racket) which permits me to use {arg_name arg_value} for default and named arguments (without the need for #:key_name). It is otherwise working all right but it is interfering with declaration of functions with variable arguments (fnname . vars). The error is simply "bad syntax". How can this be corrected? Thanks for your comments / answers.

Edit: My current code is:

(require syntax/parse/define ; for define-simple-macro
         (only-in racket [define old-define] [#%app old-#%app])
         (for-syntax syntax/stx)) ; for stx-map

(begin-for-syntax
  ;; identifier->keyword : Identifer -> (Syntaxof Keyword)
  (define (identifier->keyword id)
    (datum->syntax id (string->keyword (symbol->string (syntax-e id))) id id))
  ;; for use in define
  (define-syntax-class arg-spec
    [pattern name:id
             ;; a sequence of one thing
             #:with (norm ...) #'(name)]
    [pattern {name:id default-val:expr}                 ; rn: ch if {} needed here; since works well with [] here; 
             #:when (equal? #\{ (syntax-property this-syntax 'paren-shape))
             #:with name-kw (identifier->keyword #'name)
             ;; a sequence of two things
             #:with (norm ...) #'(name-kw {name default-val})]))

(define-syntax-parser define         ; instead of define-simple-macro; 
  [(define x:id val:expr)
   #'(old-define x val)]
  [(define (fn arg:arg-spec ...) body ...+)
   #'(old-define (fn arg.norm ... ...) body ...)])

(begin-for-syntax
  ;; for use in #%app
  (define-syntax-class arg
    [pattern arg:expr
             #:when (not (equal? #\{ (syntax-property this-syntax 'paren-shape)))
             ;; a sequence of one thing
             #:with (norm ...) #'(arg)]
    [pattern {name:id arg:expr}
             #:when (equal? #\{ (syntax-property this-syntax 'paren-shape))
             #:with name-kw (identifier->keyword #'name)
             ;; a sequence of two things
             #:with (norm ...) #'(name-kw arg)]))

(require (for-syntax (only-in racket [#%app app])))

(define-simple-macro (#%app fn arg:arg ...)
  #:fail-when (app equal? #\{ (app syntax-property this-syntax 'paren-shape))
  "function applications can't use `{`"
  (old-#%app fn arg.norm ... ...))

I am not sure which part to change. If I remove last part (define-simple-macro) the named/default arguments in {} do not work.

Further Edit: I have modified the code as follows:

(define-syntax-parser define         ; instead of define-simple-macro; 
  [(define x:id val:expr)
   #'(old-define x val)]
  [(define (fn arg:arg-spec ...) body ...+)
   #'(old-define (fn arg.norm ... ...) body ...)]
  [(define (fn . vars) body ...+)
   #'(old-define (fn . vars) body ...)]      )

and it works:

(define (testvars . vars)
  (println (list? vars))
  (for ((item vars))(println item))     )

(testvars 1 2 3)
#t
1
2
3

But why do I still need "(define-simple-macro .." part? Also, why do I need 2 "(begin-for-syntax.." definitions?

Edit again: further modification:

(define-syntax-parser define         
  [(define x:id val:expr)
   #'(old-define x val)]
  [(define (fn arg:arg-spec ...) body ...+)
   #'(old-define (fn arg.norm ... ...) body ...)]
  [(define (fn arg:arg-spec ... . vars) body ...+)                
   #'(old-define (fn arg.norm ... ... . vars) body ...)]
  )

Above finally works with both named and variable arguments, e.g. (fnname {x 0} {y 1} 10 20 30), thanks to all the help from @AlexKnauth in comments below.

1
The define macro in that question/answer doesn't support declaring "rest" arguments. When I was writing it, I didn't want to clutter the implementation with features that weren't really relevant. - Alex Knauth
Though actually, it shouldn't be very hard for you to try extending it on your own. Instead of define-simple-macro, you'd have to use define-syntax-parser, with two cases, one for when there's no rest argument, and one where there is. - Alex Knauth
The define one is the one you need to change, and that's already written with define-syntax-parser. I had forgotten, but that makes this easier. You just have to add another case to the existing define macro. - Alex Knauth
The last define-simple-macro defines the #%app macro, which re-defines what function application looks like. If you leave that out, you will be able to call (greet #:hello "hi"), but not (greet [hello "hi"]). - Alex Knauth
Also you should probably add that modified define macro as an answer, although should should make sure vars is an identifier by writing vars:id, and you should make sure you can have some normal arguments followed by ` . rst-id` too - Alex Knauth

1 Answers

1
votes

As we figured out in the comments, all you have to do is add a third case to the define macro, similar to the second case, but with a . rst after the arg:arg-spec ... in the pattern and again after the arg.norm ... ... in the template.

The second case was

  [(define (fn arg:arg-spec ...) body ...+)
   #'(old-define (fn arg.norm ... ...) body ...)]

The new case is similar, but with the . rst added

  [(define (fn arg:arg-spec ... . rst) body ...+)                
   #'(old-define (fn arg.norm ... ... . rst) body ...)]

In context it looks like this.

(define-syntax-parser define         
  [(define x:id val:expr)
   #'(old-define x val)]
  [(define (fn arg:arg-spec ...) body ...+)
   #'(old-define (fn arg.norm ... ...) body ...)]
  [(define (fn arg:arg-spec ... . rst) body ...+)                
   #'(old-define (fn arg.norm ... ... . rst) body ...)]
  )