2017-03-27 45 views
0

我的示例代碼如下:方案無法發現裏面的宏功能,而編譯

#!/usr/bin/guile -s 
!# 

(define (process body) 
    (list 'list (map (lambda (lst) 
         (list 'quote (car lst))) 
         body))) 

(defmacro macro (body) 
    (list 'quote (process body))) 

(display (macro ((foo bar) (bar baz)))) 
(newline) 

運行它,但我有錯誤,從編譯器

ERROR: Unbound variable: process 

函數中的宏應該被允許,爲什麼我得到這個錯誤?

回答

3

在宏裏面的函數是在Guile和其他大多數Scheme方言中允許的

但是,關鍵的問題是:哪些函數可用於宏在擴展過程中調出?


認爲它是這樣的:當編譯器正在處理您的代碼,它是第一家專注於將你的源代碼到的東西,可以在未來的某一時刻運行。但編譯器在編譯它們時可能不一定能夠執行那些相同的功能,同時您的宏正在運行並擴展了源代碼。

  • 爲什麼不能提供這樣的功能?那麼,一個例子是:如果函數的主體使用了宏,那麼你在定義中呢?那麼你會有一個雞/雞蛋的問題。該函數需要運行要編譯的宏(因爲在編譯時需要擴展宏體中的宏)但是宏需要編譯的函數才能運行!

(此外,可能有一些功能,你希望可在編譯時,爲您的宏幫忙,但你不希望可在運行時,使它將不會包含在您的程序可執行文件中,因爲這會浪費部署的二進制文件中的空間。)

我最喜歡的論文之一描述了這個問題,以及MzScheme採用的特定解決方案(現在稱爲球拍),是馬修弗拉特的論文"You Want It When"


所以,這是任何計劃方言與程序宏系統具有處理在某些方面出了問題,而且狡詐也不例外。

在Guile的案例中,直接記錄在Guile手冊中的一個修正是使用eval-when特殊形式,它允許您指定特定定義在哪些階段可用。

(其中「你想要它當」上面提到的論文描述了eval-when一些問題,但因爲它是狡詐手動文件,我要堅持下去什麼了。我建議你瞭解eval-when後,然後你看看Racket的解決方案,看看Guile是否提供類似的東西。)


所以你的情況,因爲你想要的process功能,可在編譯時(在宏定義使用),你可以寫:

#!/usr/bin/guile -s 
!# 

(eval-when (expand) 
    (define (process body) 
    (list 'list (map (lambda (lst) 
         (list 'quote (car lst))) 
        body)))) 

(defmacro macro (body) 
    (list 'quote (process body))) 

(display (macro ((foo bar) (bar baz)))) 
(newline)