在上期討論中我們介紹了Scala Macros,它可以說是工具庫編程人員不可或缺的編程手段,可以實現(xiàn)編譯器在編譯源代碼時對源代碼進(jìn)行的修改、擴展和替換,如此可以對用戶屏蔽工具庫復(fù)雜的內(nèi)部細(xì)節(jié),使他們可以用簡單的聲明方式,通過編譯器自動產(chǎn)生鋪墊代碼來實現(xiàn)工具庫中各種復(fù)雜的類型、對象及方法函數(shù)的構(gòu)建。雖然Def Macros可能具備超強的編程功能,但同時使用者也普遍認(rèn)為它一直存有著一些嚴(yán)重的詬病:包括用法復(fù)雜、容易犯錯、運算行為難以預(yù)測以及沒用完善的集成開發(fā)環(huán)境工具(IDE)支持等。這些惡評主要是因為Def Macros和編譯器scalac捆綁的太緊,使用者必須對編譯器的內(nèi)部運作原理和操作函數(shù)有比較深刻的了解。加之Def Macros向用戶提供的api比較復(fù)雜且調(diào)用繁瑣,其中比較致命的問題就是與scalac的緊密捆綁了:因為Def Macros還只是一項實驗性功能,沒有scala語言規(guī)范文件背書,肯定會面臨升級換代。而且scala本身也面臨著向2.12版本升級的情況,其中dotty就肯定是scalac的替代編譯器。Scalameta是根據(jù)scala語言規(guī)范SIP-28-29-Inline-Macros由零重新設(shè)計的Macros編程工具庫。主要目的就是為了解決Def Macros所存在的問題,而且Jetbrains的IntelliJ IDEA 2016.3 EAP對Scalameta已經(jīng)有了比較好的支持,能為使用者帶來更簡單、安全的Macros編程工具。
我在介紹了Slick之后立即轉(zhuǎn)入Scala Macros是有一些特別目的的。研究FRM Slick乃至學(xué)習(xí)泛函編程的初衷就是希望能為傳統(tǒng)的OOP編程人員提供更簡單易用的泛函庫應(yīng)用幫助,使他們無須對函數(shù)式編程模式有太深刻了解也能使用由函數(shù)式編程模式所開發(fā)的函數(shù)庫。實現(xiàn)這個目標(biāo)的主要方式就是Macros了。希望通過Macros的產(chǎn)生代碼功能把函數(shù)庫的泛函特性和模式屏蔽起來,讓用戶能用他們習(xí)慣的方式來定義函數(shù)庫中的類型對象、調(diào)用庫中的方法函數(shù)。
Macros功能實現(xiàn)方式(即編譯時的源代碼擴展compile time expansion)由兩個主要部分組成:一是在調(diào)用時擴展(on call expansion),二是申明時擴展即注釋(annotation)。這兩種方式我們在上一篇討論里都一一做了示范。通過測試發(fā)現(xiàn),Scalameta v1.x只支持注釋方式。這事動搖了我繼續(xù)探討的意愿:試想如果沒了”Implicit Macros“,“Extractor Macros“這些模式,會損失多少理想有趣的編碼方式。通過與Scalameta作者溝通后得知他們將會在Scalameta v2.x中開始支持全部兩種模式,因此決定先介紹一下Scalameta v1.x,主要目的是讓大家先熟悉了解Scalameta新的api和使用模式。我們可以把上次Def Macros的Macros Annotations示范例子在Scalameta里重新示范一遍來達(dá)到這樣的目的。
雖然Scalameta是從頭設(shè)計的,但是它還是保留了許多Def Macros的思想,特別是沿用了大部分scala-reflect的quasiquote模式。與Def Macros運算原