Я пытаюсь записать макрос, который определяет специальный класс структуры данных с присоединенными функциями.
Я знаю, что это возможно; это сделано многократно на самом базовом языке.
Как определенный пример, как я определил бы define-struct
макрос в самой Схеме. Это должно создать make-struct
, struct-<<field>>
, и т.д. функции.
Я пытался делать это использование define
, однако, это только определяет функцию в лексическом контексте макроса.
Как я могу на самом деле определить функцию в макросе?
Ключом для ответа является данные-> синтаксис
. Основная идея состоит в том, что вы хотите взять некоторые случайные данные и превратить их в синтаксис - в этом случае превратить символ в идентификатор. Идентификатор - это в основном символ с некоторой лексической информацией, которая (очень грубо) указывает, как он связан. Используя синтаксис datum->
, вы можете сделать именно это: он ожидает существующий фрагмент синтаксиса, из которого он копирует привязку, и данные (здесь символ), которые являются значением, содержащимся в синтаксическая оболочка.
Вот пример, демонстрирующий инструмент, похожий на 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))