神奇的Scala Macro之旅(一)- 何時用宏 神奇的Scala Macro之旅(二)- 一個實例

在Lisp語言中,macro是一個神器,能夠「動態的生成代碼」,而後被執行,這種方式給到Lisp無限的表達能力。除Lisp以外,不多有語言支持Macro這個特性,我記得 GWT之中曾經有一個相似的Generator的概念,能夠在編譯期間提供必定的代碼生成能力(GWT Project),不少GWT的高級特性都是經過這個語言特性而得以實現的,譬如:html

  • UiBinder 能夠實現申明式的 UI 開發。android

  • ImageBundle 簡單的實現不少圖片的聚合,從而減小round-trip,提升網頁裝載速度。程序員

Scala自2.10開始嘗試支持macro編程,2.11基本步入」成熟「階段,2.12中基本穩定,不過,在最新的路線圖中,Scala之父,Martin再次表現了將在將來的scala版本(Dotty)中從新設計macro,採用基於scalameta的一套編譯器「中立」的元信息模型,試圖讓macro變得更簡單、容易。所以,嘗試 macro,目前仍然具備很大的風險性,或許隨着scala語言的將來版本,現有的macro也須要從新遷移、實現。正則表達式

 

What is Macro?

簡而言之,macro就是讓你的代碼具有有生成代碼的能力,最簡單的macro能夠算上 C/C++ 的 ##define, 最先的 C 程序員就普遍的使用 define 來生成代碼。(嚴格意義上,C的define並不算是 macro)sql

 

Why using Macro?

程序員喜歡寫有挑戰的代碼,而確定不喜歡 Copy & Paster,或者甚至連Copy Paste都算不上的重複性代碼,一個很簡單的例子,就是Java程序員都最熟悉的以下代碼:數據庫

public class User { 
 private String name;
 private int age;
 public String getName(){ return this.name;}
 public void setName(String name){ this.name = name;}
 public int getAge(){ return this.age; }
 public void setAge(int age){ this.age = age; }
 public String toString(){ return "name:" + name + "/age:" + age;}  public int hashCode(){ return name.hashCode ^ age;}
 public boolean equalus(Object other){ return ......} }

這段代碼是很是有Java風格的一段代碼,每個Java程序員都很熟悉,這聽說是一種設計模式,叫作」JavaBeans」, 這個模式有不少的延續:編程

  • 基本上每一個IDE,都會有一個工具來幫你自動的生成 getter, setter, toString, hashCode等工具json

  • 有一個 Lombok 的開源項目,幫你來生成這些代碼。設計模式

  • scala 常常用 case class User(name:String, age: Int)來鄙視Javacookie

  • Kotlin 常常用 data class User(val name: String, val age: Int)來鄙視Java

這是一個很好的案列:

  • 這段代碼的「乾貨」不多,用一段爲代碼data class User(val name: String, val age: Int就能夠表述清楚的東西,咱們卻須要囉囉嗦嗦的寫一大段代碼。沒有程序員喜歡這種差事,除非,老闆真的是按代碼行數付工資的。

  • Lombok可讓你編寫不多的代碼,他來負責生成剩下的代碼。

  • Eclipse/JetBrains,可讓你寫少許的代碼,他來負責生成剩下的代碼。

  • Scala/Kotlin這兩個編譯器,可讓你寫少許的代碼,他來負責生成剩下的代碼。

當咱們只須要寫最爲」核心「的那一步代碼,而剩下的代碼,徹底能夠按照一個模式去自動生成時,咱們的工做效率就大幅度提高了,而言,Less is more,更少的代碼就意味着更快速的代碼閱讀、評審,更少的BUG,這一切,都會帶給程序員更大的幸福感。

 

實際上,還有一種方式,也多是最好的方式,就是利用macro,來幫你生成代碼。固然,要這門語言,支持macro。從目前的狀況來看,支持macro的語言仍是很是有限的。scala做爲學術派的表明,頗有幸能夠提供macro的能力。

 

When to using Macro?

記得在一本Clojure的書中說過何時可使用Macro?

  • 若是你可使用普通的函數實現,不要用macro

  • 若是你能夠用其餘方式實現,不要用macro

  • 實在找不到辦法,嘗試一下macro。

其實全部的一切,都是在於Macro的複雜性。若是說Clojure代碼有如天書,那麼,去生整天書的macro代碼,就更是天書中的天書了。 這個放到Scala中,只會變得更爲複雜,緣由無二:Scala有一個很是複雜的類型體系,而Macro中你須要和這個無比複雜的類型體系打交道。因此,若是你對Scala的瞭解程度還不夠深刻的話,Macro也會是無從下手的。不過,這也是一個不錯的試金石,若是你想說你對scala的瞭解程度很好的話,那麼不妨挑戰一下Macro。這絕對是一個既複雜,但又超級強大的工具。Macro的強大,有的時候,是無敵的,這些特性,只能在Macro中完成,你沒有更好的被的辦法。

How can Macros Do?

在本篇文章中,我不許備介紹 How to writing Macro?這個我會嘗試在後續的文章中介紹,我會先介紹使用 macro,咱們能夠作些什麼?

  • 更有效的日誌性能。咱們常常告誡,不要讓日誌影響性能:

if(logger.isDebugEnabled) logger.debug("message is heavy in business: ");

爲何不可以直接用以下的代碼來替代呢?logger.debug(message),既知足性能的需求,又簡潔呢? Macro Can

  • 編譯時期進行更好的語法檢查。

"\d+".r.test("1234")複雜的正則表達式,每每須要複雜的調試工做,若是編譯器能幫助我檢查語法錯誤,相信可讓代碼開發變得更爲簡單。其實,除了正則表達式,咱們還能夠檢查原代碼中的日期常量、時間常量等,固然,若是在作一下優化,自動的將複雜的字符串常量預編譯爲構造值,可能還會得到更好的性能。Macro Can

  • 是否能夠在編譯時,對咱們的SQL語句,自動的進行語法檢查?更好的,連接到一個數據庫,進行預處理檢查,看看是否有錯誤的表名、字段名拼寫?是否有語法性的錯誤?早期的4gl語言,就有這個特性,仍是很實用的。

    Macro Can
  • 爲咱們的數據對象(Case ClassData Class)自動生成 toJSON, toXML, toProtobuf, toThrift等工具方法。反射是一種方式,因此有fast-son, fast-xml等框架?有沒有可能有比fast-jsonfast-xml更加fast的方式呢?

    有的,Macro Can
  • 還能幹什麼?更爲通常的,若是你要實現功能抽象成最小化的一個Core,而後你告訴一個程序員,如何實現剩下的功能,例如,如何編寫gettersettertoString方法,讓他來幫你實現代碼。那麼,你能夠用 Macro 來直接完成,再也不須要一個程序員的工做。這個也算是 AI 的一個近似值了把。

    Macro can write code for You

 

結合Scala的implicit,macro有很強大的功能,我會結合個人開源框架 scala-sql 應用的 macro 特性,給你繼續Scala的神奇Macro之旅。

 

參考:

神奇的Scala Macro之旅(二)- 一個實例

轉自:神奇的Scala Macro之旅

相關文章
相關標籤/搜索