Как определить функции с помощью макросов Racket? - PullRequest
8 голосов
/ 23 мая 2010

Я пытаюсь написать макрос, который определяет специальный класс структуры данных со связанными функциями.

Я знаю, что это возможно; это делается несколько раз на самом базовом языке.

В качестве конкретного примера, как бы я определил макрос define-struct в самой Схеме. Необходимо создать функции make-struct, struct-<<field>> и т. Д.

Я попытался сделать это, используя define, однако, это определяет только функцию в лексической области видимости макроса.

Как я могу на самом деле определить функцию в макросе?

1 Ответ

16 голосов
/ 23 мая 2010

Ключ для ответа datum->syntax. Основная идея заключается в том, что вы хотите взять некоторые случайные данные и превратить их в синтаксис - в этом случае превратить символ в идентификатор. Идентификатор - это в основном символ с некоторой лексической информацией, которая (очень приблизительно) указывает, как он связан. Используя datum->syntax, вы можете сделать именно это: он ожидает существующий фрагмент синтаксиса, из которого копирует привязку, и элемент данных (здесь символ), который является значением, содержащимся в оболочке синтаксиса.

Вот пример, который демонстрирует define-struct -подобный инструмент, использующий это:

#lang scheme
;; implements a defstruct-like macro that uses association lists
(define-syntax (defstruct-lite stx)
  (syntax-case stx ()
    [(defstruct-lite name field ...)
     (let ([make-id
            (lambda (template . ids)
              (let ([str (apply format template (map syntax->datum ids))])
                (datum->syntax stx (string->symbol str))))])
       (with-syntax ([make-name (make-id "make-~a" #'name)]
                     [name?     (make-id "~a?" #'name)]
                     [(arg ...) (generate-temporaries #'(field ...))]
                     [(name-field ...)
                      (map (lambda (f) (make-id "~a-~a" #'name f))
                           (syntax->list #'(field ...)))])
         #'(begin
             (define (make-name arg ...) (list 'name (cons 'field arg) ...))
             (define (name? x) (and (pair? x) (eq? 'name (car x))))
             (define (name-field x)
               (and (name? x) (cdr (assq 'field (cdr x)))))
             ...)))]))

А вот пример его использования:

(defstruct-lite point x y)
(point-y (make-point 1 2))
...