在上期討論中我們介紹了Scala Macros,它可以說是工具庫編程人員不可或缺的編程手段,可以實(shí)現(xiàn)編譯器在編譯源代碼時(shí)對(duì)源代碼進(jìn)行的修改、擴(kuò)展和替換,如此可以對(duì)用戶屏蔽工具庫復(fù)雜的內(nèi)部細(xì)節(jié),使他們可以用簡(jiǎn)單的聲明方式,通過編譯器自動(dòng)產(chǎn)生鋪墊代碼來實(shí)現(xiàn)工具庫中各種復(fù)雜的類型、對(duì)象及方法函數(shù)的構(gòu)建。雖然Def Macros可能具備超強(qiáng)的編程功能,但同時(shí)使用者也普遍認(rèn)為它一直存有著一些嚴(yán)重的詬病:包括用法復(fù)雜、容易犯錯(cuò)、運(yùn)算行為難以預(yù)測(cè)以及沒用完善的集成開發(fā)環(huán)境工具(IDE)支持等。這些惡評(píng)主要是因?yàn)镈ef Macros和編譯器scalac捆綁的太緊,使用者必須對(duì)編譯器的內(nèi)部運(yùn)作原理和操作函數(shù)有比較深刻的了解。加之Def Macros向用戶提供的api比較復(fù)雜且調(diào)用繁瑣,其中比較致命的問題就是與scalac的緊密捆綁了:因?yàn)镈ef Macros還只是一項(xiàng)實(shí)驗(yàn)性功能,沒有scala語言規(guī)范文件背書,肯定會(huì)面臨升級(jí)換代。而且scala本身也面臨著向2.12版本升級(jí)的情況,其中dotty就肯定是scalac的替代編譯器。Scalameta是根據(jù)scala語言規(guī)范SIP-28-29-Inline-Macros由零重新設(shè)計(jì)的Macros編程工具庫。主要目的就是為了解決Def Macros所存在的問題,而且Jetbrains的IntelliJ IDEA 2016.3 EAP對(duì)Scalameta已經(jīng)有了比較好的支持,能為使用者帶來更簡(jiǎn)單、安全的Macros編程工具。

  我在介紹了Slick之后立即轉(zhuǎn)入Scala Macros是有一些特別目的的。研究FRM Slick乃至學(xué)習(xí)泛函編程的初衷就是希望能為傳統(tǒng)的OOP編程人員提供更簡(jiǎn)單易用的泛函庫應(yīng)用幫助,使他們無須對(duì)函數(shù)式編程模式有太深刻了解也能使用由函數(shù)式編程模式所開發(fā)的函數(shù)庫。實(shí)現(xiàn)這個(gè)目標(biāo)的主要方式就是Macros了。希望通過Macros的產(chǎn)生代碼功能把函數(shù)庫的泛函特性和模式屏蔽起來,讓用戶能用他們習(xí)慣的方式來定義函數(shù)庫中的類型對(duì)象、調(diào)用庫中的方法函數(shù)。

  Macros功能實(shí)現(xiàn)方式(即編譯時(shí)的源代碼擴(kuò)展compile time expansion)由兩個(gè)主要部分組成:一是在調(diào)用時(shí)擴(kuò)展(on call expansion),二是申明時(shí)擴(kuò)展即注釋(annotation)。這兩種方式我們?cè)谏弦黄懻摾锒家灰蛔隽耸痉?。通過測(cè)試發(fā)現(xiàn),Scalameta v1.x只支持注釋方式。這事動(dòng)搖了我繼續(xù)探討的意愿:試想如果沒了”Implicit Macros“,“Extractor Macros“這些模式,會(huì)損失多少理想有趣的編碼方式。通過與Scalameta作者溝通后得知他們將會(huì)在Scalameta v2.x中開始支持全部?jī)煞N模式,因此決定先介紹一下Scalameta v1.x,主要目的是讓大家先熟悉了解Scalameta新的api和使用模式。我們可以把上次Def Macros的Macros Annotations示范例子在Scalameta里重新示范一遍來達(dá)到這樣的目的。

  雖然Scalameta是從頭設(shè)計(jì)的,但是它還是保留了許多Def Macros的思想,特別是沿用了大部分scala-reflect的quasiquote模式。與De