2016-09-05 9 views
2

私は(ちょうど下の例では簡略化のために拡大)を定義するだけのラケットのように動作するマクロを記述しようとしているが、いくつかの方法で完全に展開ラケットの手続きを処理しています:ラケット:ローカル・拡大再帰的定義

(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をNDにおけるモジュール識別子:合計

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に手続きを展開する)問題を解決するが、他のものを作成します。

モジュール:で定義のためのアウトコンテキスト識別子:和

和の定義を指します。なぜなら、エクスパンダは、現在のコンテキストではheadではなく、ctxの識別子に識別子をバインドするからです。

直感的にはまれな問題ではないようですが、ネットワーク上で解決策を見つけることができませんでした。私は何とかlocal-expand/capture-liftssyntax-local-lift-expressionを使うべきだと思っていましたが、正しく使用する方法はわかりません。誰かが何が起こっているかを明確にしたり、それを修正するヒントを与えたりできますか?

+1

別時々、便利なトリック:パターンマッチ、その後、結合していない変数をバインドラムダに 'ローカルexpand'を呼び出します体を出すための結果。 –

答えて

2

はのトップレベル(REPL)で、あなたの最初のプログラムを試してみましょう:REPLで

#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 

これはあなたのプログラムは、トップレベルで動作することを示しています。

モジュールコンテキスト で同じアプローチが機能しない理由は、式 の部分展開を使用して式を展開する前に定義を検出するということです。言い換えれば は、それがsumの定義に展開#%module-begin ことを伝える必要があります拡大/定義が、モジュールレベルですべて バインドされた識別子を検出した#%module-beginまでlocal-expandの使用 を遅らせる必要があります。

これは、2段階のアプローチを提案する:

#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

関連する問題