次元超越锅纺! Macro in Racket

Intro

最近寫代碼感覺非常的不順。 之前看自己一個月以來寫的代碼真的是被自己惡心到了吭狡。 感覺我的Racket 還是沒有能夠掌握主要特性。 所以大概就花了一周左右的時間把Macro拿出來學(xué)了一下丈莺, 今年的PL課上很可惜沒有時間在課上講這個划煮。


如果說編程語言是魔咒學(xué)的話, 這個Macro 絕對是次元魔法缔俄, 特別是在Racket語言當(dāng)中弛秋。 簡單的說, Macro就是能讓你在正式編譯之前的,先對你的代碼進(jìn)行一次預(yù)編譯俐载, 在這個時候把你代碼中的一些符合條件的規(guī)則蟹略,全部替換成你需要的代碼,或者說去生成你想要的代碼遏佣。 之前寫過C語言的宏的話應(yīng)該還是對這個東西不陌生的挖炬,但是在大部分的語言中,宏只能做一些非常Ad-hoc的替換状婶,如果你的替換方式非常的復(fù)雜茅茂, 這時候就會很難寫,也很容易出Bug太抓。但是在Racket之類的Scheme系語言當(dāng)中,宏系統(tǒng)基本上就是能讓你直接在編譯期運(yùn)行你寫的scheme/racket代碼令杈。得益于這種強(qiáng)大的能力你可以輕松進(jìn)入高次元走敌,在那里直接對racket這個編程語言本身進(jìn)行擴(kuò)展得到你想要的語法特性。Racket 是 Racket的meta language

交換

在編程中我們經(jīng)常會需要去交換兩個變量的值逗噩。這個在命令式語言中非常的簡單掉丽, 可以直接用/

temp = a;
a = b;
b = tmp;

在racket當(dāng)中我們也不難能夠?qū)懗鱿旅娴腸ode

(let ([tmp x])
  (set! x y)
  (set! y tmp))

雖然racket作為函數(shù)式語言這種命令式編程語言中的“變量”,其實(shí)在racket中非常的少用异雁。
寫過匯編的都知道其實(shí)匯編里面描述交換的時候可以直接使用XCHG捶障。這個語義其實(shí)非常的好, 我們希望在我們的編程語言中能夠?qū)崿F(xiàn)這一種語法結(jié)構(gòu)纲刀。根據(jù)metacircular interpretor 的思想,如果我們把racket當(dāng)作racket本身的meta language, 我們需要做的就是把xchg這個語法“翻譯”到上面的這這段racket code项炼。
這種替換非常的直接, 我們可以用define-syntax-rule來直接在我們的代碼中給racket添加一條語法規(guī)則

(define-syntax-rule (xchg x y)
  (let ([tmp x])
    (set! x y)
    (set! y tmp)))

當(dāng)然你如果把上面的define-syntax-rule改成define代碼的運(yùn)行結(jié)果不會發(fā)生任何的改變示绊。
那和函數(shù)有什么區(qū)別锭部?
其實(shí)這里編譯器就相當(dāng)于在編譯值之前先把你所有代碼中的xchg 修改成了(let ...)你的代碼本身發(fā)生的變化,而如果定義函數(shù)的方式來實(shí)現(xiàn)面褐,這里實(shí)際上就是一個函數(shù)調(diào)用拌禾,會新建一個函數(shù)棧在運(yùn)行時,但是你的代碼一旦寫好了展哭,自然是不會被編譯器修改的湃窍。
這里很明顯的感覺就是使用s 表達(dá)式的優(yōu)勢, 因?yàn)槲覀兊拇a相當(dāng)于已經(jīng)天然的被parse好了闻蛀,所以很容易看清語法的邊界, 這種代碼和數(shù)據(jù)(list)的同構(gòu)性您市,讓寫宏的人會更加清晰觉痛。

使用racket編寫語法謂詞

如果我們要使用racket來寫一個簡單的解釋器,第一步需要做的就是定義目標(biāo)語言的predicate, 這樣我們就可以配合quasi quoting和pattern match來比較輕松的定義語言的語法了,比如簡單的lambda的語法如下

lambda-exp :: x
            |  (lambda-exp lambda-exp)
            |  (lamdba  (x ...) lambda-exp)

where x is symbol

使用racket predicate來描述

(define (lambda-exp? e)
  (match e
    [(? symbol?) #t]
    [`(lambda (,x ...) ,(? lambda-exp?)) #t]
    [`(,(? lambda-exp? e1) ,(? lambda-exp?)) #t]))

不過感覺這一學(xué)期下來給我的感受就是墨坚,其實(shí)大部分人對于quasi quoting pattern match的閱讀能力幾乎是0........很多人到了期末都沒徹底搞明白秧饮。
這個其實(shí)是一種在racket當(dāng)中比較常見的做法但是確實(shí)會帶來一些的閱讀困難,同時這種語法結(jié)構(gòu)其實(shí)并不是那么容易去擴(kuò)展的泽篮, 在簡單單一的一組EBNF規(guī)則中其實(shí)是看不太出這個盗尸。但是如果我希望做到類似與extend一組規(guī)則的時候這種結(jié)構(gòu)其實(shí)就會顯的比較亂了。 如果我們是要寫一個編譯器帽撑,在常見的教程和方法中都會要求有非常多級的IR泼各,特別是如果我們使用nano-pass style, 就需要非常對級不同的IR亏拉,每級只是對中間的某一個小部分進(jìn)行修改扣蜻,這時候純使用predicate就會有點(diǎn)難受了。

這時候我們可以使用宏及塘,在racket中定義一個語法結(jié)構(gòu)叫define-ir , 我們可以編寫類似EBNF的代碼莽使,然后macro expand之后我們就可以自動得到我們想要的predicate

使用宏生成語法謂詞

1. syntax

這個其實(shí)還是比較復(fù)雜的,可以一點(diǎn)一點(diǎn)來笙僚, 先看一種比較簡單的情況:
我們的宏會take 一個list,最終在編譯之后把list上的所有終結(jié)符(terminate symbol) 全部替換成quasi pattern,也就是說比如
'(symbol symbol)在 expand 之后會變成 `(,(? symbol?) ,(? symbol?))

對于宏我們的本質(zhì)就是把代碼結(jié)構(gòu)就像list一樣進(jìn)行演算芳肌。盡管我們的s表達(dá)式非常的像list,但是很明顯'(symbol symbol)只是一個list data, 如何在racket表示一段代碼'(symbol symbol)呢肋层?
其實(shí)和quote有點(diǎn)像我們可以使用syntax quote,#'關(guān)鍵字
#'(symbol symbol)
這樣表示的就是一段syntax對象而不是一個list對象了亿笤。
我們甚至還可以使用quasi syntax, 這樣我們就可以在一個syntax object中間使用#,來混入racket代碼了
(define x #'c)
#`(a b #,x)

我們也可以用racket的內(nèi)置函數(shù)synatx->datum \ datum->syntax來完成這種data 和syntax的互相轉(zhuǎn)化

> (require racket/syntax)
> (syntax->datum #'symbol?)
'symbol?
> (syntax->datum #'(a b c))
'(a b c)
> (datum->syntax (current-syntax-context) 'x)
#<syntax x>

<to be continue>


一些代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市栋猖,隨后出現(xiàn)的幾起案子净薛,更是在濱河造成了極大的恐慌,老刑警劉巖蒲拉,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肃拜,死亡現(xiàn)場離奇詭異,居然都是意外死亡全陨,警方通過查閱死者的電腦和手機(jī)爆班,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辱姨,“玉大人柿菩,你說我怎么就攤上這事∮晏危” “怎么了枢舶?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵懦胞,是天一觀的道長。 經(jīng)常有香客問我凉泄,道長躏尉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任后众,我火速辦了婚禮胀糜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒂誉。我一直安慰自己教藻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布右锨。 她就那樣靜靜地躺著括堤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绍移。 梳的紋絲不亂的頭發(fā)上悄窃,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機(jī)與錄音蹂窖,去河邊找鬼轧抗。 笑死,一個胖子當(dāng)著我的面吹牛瞬测,可吹牛的內(nèi)容都是我干的鸦致。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼涣楷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抗碰?” 一聲冷哼從身側(cè)響起狮斗,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弧蝇,沒想到半個月后碳褒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡看疗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年沙峻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片两芳。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡摔寨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怖辆,到底是詐尸還是另有隱情是复,我是刑警寧澤删顶,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站淑廊,受9級特大地震影響逗余,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜季惩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一录粱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧画拾,春花似錦啥繁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脂凶,卻和暖如春宪睹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚕钦。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工亭病, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘶居。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓罪帖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邮屁。 傳聞我的和親對象是個殘疾皇子整袁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359