Racket macro to define functions given a repeated pattern

早过忘川 提交于 2019-12-12 13:17:50

问题


The problem is quite difficult to explain because I need to collect my thoughts, so bear with me. I've been able to reduce the problem to a minimal example for illustrative purposes. The example will not make any sense as to what this would be useful for, but I digress. Say I want to extend the racket language to write things that look like this:

(define-something
  (['a] 'whatever)
  (['b 'c] 'whatever2))

Between the square brackets is a sequence of one or more symbols, followed by a sequence of racket expressions (the whatever's, which are not important for the problem statement)

The example would match a macro that looks something like this:

(define-syntax (define-something stx)
  (syntax-case stx ()
    [(_  ([symb ...] body ...) ...)
     #'()]))

Actually here we match 0 or more symbols, but we can assume there is always going to be at least one.

In the macro's body I want to generate function definitions using the concatenated symbols as the identifier. So for our silly example the macro would expand to something like:

(define (a) 'whatever)
(define (bc) 'whatever2)

I have found a similar question where the poster generates functions using a pre-defined list of strings, but I am not that fluent with macro's so I have not been able to translate the concepts to solve my problem. I thought perhaps I could try generating a similar list (by concatenating the symbols) and applying their tactic, but I've been getting way too confused with all the ellipses in my macro definition. I'm also a bit confused about their use of an ellipsis in with-syntax.


回答1:


It’s possible to solve this with with-syntax and syntax-case, but the easiest way to do this is by using syntax-parse’s syntax classes. By defining a syntax class that parses a list of symbols and produces a single concatenated identifier, you can lift the symbol parsing out of the macro body:

(require (for-syntax syntax/parse
                     racket/string))

(begin-for-syntax
  (define-syntax-class sym-list
    #:attributes [concatenated-id]
    (pattern (~and stx (sym:id ...))
             #:attr concatenated-id
             (let* ([syms (syntax->datum #'(sym ...))]
                    [strs (map symbol->string syms)]
                    [str (string-append* strs)]
                    [sym (string->symbol str)])
               (datum->syntax #'stx sym #'stx #'stx)))))

Now you can define your macro pretty easily:

(define-syntax (define-something stx)
  (syntax-parse stx
    [(_ (syms:sym-list body ...) ...)
     #'(begin
         (define (syms.concatenated-id) body ...)
         ...)]))

Note that this uses unquoted symbols in the name clause, so it would work like this:

(define-something
  ([a] 'whatever)
  ([b c] 'whatever2))

The names can’t be expressions that evaluate to symbols because the information needs to be known at compile-time to be available to macro expansion. Since you mentioned in a comment that this is for an FRP-like system, your signal graph will need to be static, like Elm’s is for example. If you want the ability to construct a dynamic signal graph, you’ll need a more complex strategy than macros since that information will need to be resolved at runtime.



来源:https://stackoverflow.com/questions/34776548/racket-macro-to-define-functions-given-a-repeated-pattern

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!