2016-09-05 5 views
2

Я пытаюсь написать макрос Behaving так же, как рэкет определить, но обработки полностью укомплектованные процедуры ракетки какими-то образом (только расширение для упрощения в примере ниже):Ракетки: локальные расширяющиеся рекурсивные определения

(define-syntax (define/expand stx) 
    (syntax-case stx() 
    [(_ (head args ...) body body-rest ...) 
     (let* ([define/racket (syntax/loc stx (define (head args ...) body body-rest ...))] 
       [fully-expanded (local-expand define/racket 'top-level (list))]) 
      fully-expanded)] 
    [(_ id expr) (syntax/loc stx (define id expr))])) 

Все хорошо, если рекурсивное определение не будет выполнено:

(define/expand (sum n) 
    (if (<= n 0) 
     0 
     (+ n (sum (- n 1))))) 

Забегая вызывает ошибку

сумму: unbou го идентификатора модуля в: сумма

указывая вызов из sum (не определение). Очевидно, что определение sum не фиксируется локальным расширителем. Я попытался простым способом ее фиксации: создание нового контекста локального определения и связывание head в него:

(define-syntax (define/expand stx) 
    (syntax-case stx() 
    [(_ (head args ...) body body-rest ...) 
     (let* ([ctx (syntax-local-make-definition-context)]   ; <- These two lines added 
       [_ (syntax-local-bind-syntaxes (list #'head) #f ctx)] ; <--/ 
       [define/racket (syntax/loc stx (define (head args ...) body body-rest ...))] 
       [fully-expanded (local-expand define/racket 'top-level (list) ctx)]) 
      fully-expanded)] 
    [(_ id expr) (syntax/loc stx (define id expr))])) 

Это решает проблему (локальный расширены успешно расширяет процедуру в define-values), но создает другую:

модуля: вне контекста идентификатора для определения в: сумме

указывая определения суммы. Причина в том, что расширитель связывает идентификаторы с одним в ctx вместо head в текущем контексте.

Интуитивно это не похоже на редкую проблему, но я не смог найти решение по сети. Я думал, что мне нужно как-то использовать local-expand/capture-lifts и syntax-local-lift-expression, но я не понимаю, как правильно его использовать. Может ли кто-нибудь уточнить, что происходит и/или дать подсказку, как это исправить?

+1

Другой порой полезный трюк: позвонить Локаль--expand' на лямбда связывания несвязанных переменных, а затем сопоставления с образцом результат, чтобы вывести организм. –

ответ

2

Давайте попробуем свою первую программу в верхнем уровне (Отв):

#lang racket 
(define-syntax (define/expand stx) 
    (syntax-case stx() 
    [(_ (head args ...) body body-rest ...) 
    (let* 
     ([define/racket (syntax/loc stx (define (head args ...) body body-rest ...))] 
      [fully-expanded (local-expand define/racket 'top-level (list))]) 
     fully-expanded)] 
    [(_ id expr) 
    (syntax/loc stx (define id expr))])) 

, а затем в РЕПЛ:

Welcome to DrRacket, version 6.6.0.3--2016-07-28(-/f) [3m]. 
Language: racket, with debugging [custom]; memory limit: 1024 MB. 
> (define/expand (sum n) 
    (if (<= n 0) 
     0 
     (+ n (sum (- n 1))))) 
.#<syntax:3:2 (define-values (sum) (lambda ...> 
> (sum 5) 
15 

Это показывает, что ваша программа работает на высшем уровне.

Причина того же подхода не работает в контексте модуля заключается в том, что #%module-begin использует частичное расширение форм для определения определений перед расширением выражений. Другими словами определить/расширения должен сказать, что #%module-begin он расширяется в определение sum, но не должно задерживать использование из local-expand до #%module-begin обнаружил все связанные идентификаторы на уровне модуля.

Это предполагает два шага:

#lang racket 

(define-syntax (delay-expansion stx) 
    (syntax-case stx() 
    [(_delay-expansion more ...) 
    (let ([fully-expanded (local-expand #'(lambda() more ...) 'module (list))]) 
     (display fully-expanded) 
     fully-expanded)])) 

(define-syntax (define/expand stx) 
    (syntax-case stx() 
    [(_ (head args ...) body body-rest ...) 
    (syntax/loc stx 
     (define (head args ...) 
     ((delay-expansion 
      body body-rest ...))))] 
    [(_ id expr) 
    (syntax/loc stx 
     (define id expr))])) 


(define/expand (sum n) 
    (if (<= n 0) 
     0 
     (+ n (sum (- n 1))))) 

(sum 5) 

увидеть здесь: https://groups.google.com/d/msg/racket-users/RB3inP62SVA/54r6pJL0wMYJ

+0

Ничего себе, хороший трюк, большое вам спасибо! Он также отвечает на некоторые другие вопросы. – dvvrd