Facebook開源全新靜態語言Skip,效能如何你不瞭解下?
Skip 是 Facebook 開發的一個實驗性質的程式語言,從 2015 年到 2018 年開發了三年。
作為一個研究專案,Skip 的主要目標是探索支援準確、高效、基於記憶化(memoization)的快取和快取失效的語言和執行時。通過靜態型別系統追蹤可變性,Skip 做到了前面提到的特性,同時它也支援現代語言特徵,例如 trait、泛型與子型別。
據專案介紹,Skip 專案 2018 年已經結束,Facebook 不再積極開發。至於如何繼續保持該語言的穩定性,文章表示,Facebook 將會開源該語言,使程式語言研究社群在語言設計與實現、編譯器和庫的基礎上進行研究和構建。
該語言的主要設計者是 Facebook 語言團隊負責人 Julien Verlaguet,他維護了該語言、編譯器與庫。
Skip 概覽
Skip 是一種通用程式語言,它跟蹤副作用(side effect),提供反應無效的快取、ergonomic 和安全的並行化以及高效的垃圾回收。Skip 是靜態型別的,它使用 LLVM 提前編譯,生成高度優化的可執行檔案。
反應無效的快取
Skip 主要的新特性是精準跟蹤副作用,包括值的可變性和區分非確定性資料來源與能夠提供反應無效的資料來源(當資料發生變化時告訴 Skip)。當 Skip 的型別系統能夠證明給定函式邊界沒有副作用時,開發人員可以選擇安全地記憶該計算,並在執行時確保當底層資料發生變化時,以前快取的值無效。
安全的並行化
Skip 支援兩種互補的併發程式設計,由於它跟蹤副作用,這兩種程式設計都避免了常見的執行緒安全問題。首先,Skip 支援使用 async/await 語法的 ergonomic 非同步計算。由於 Skip 跟蹤副作用,非同步計算不能引用可變狀態,因此可以安全地並行執行(所以獨立的非同步計算可以並行繼續)。其次,Skip 有可用於直接平行計算的 API,同樣利用其追蹤副作用的特型來防止執行緒安全問題,如共享對可變狀態的訪問。
高效和可預測垃圾回收器(GC)
Skip 使用一種新型方法進行記憶體管理,即結合典型的垃圾回收特性和更直接的線性分配模式。由於 Skip 追蹤副作用,垃圾回收器僅需要掃描從底層計算處可訪問的記憶體。在實踐中,這意味著開發者能夠使用可預測 GC 寫程式碼。
面向函式和麵向物件的混合語言
Skip 的一大特點是混合了面向函式和物件的方法,整合二者形成一種具備聚合力的語言。與函式式語言類似,Skip 表達能力強,支援抽象的資料型別、模式匹配、簡單的匿名函式(easy lambdas)和高階函式等。與命令式面向物件的語言相似,Skip 支援具備繼承性的類別、可變物件、迴圈和 early return。Skip 還整合了「systems」語言的部分理念,支援低開銷抽象、通過值類的緊湊儲存佈局,以及確保利用靜態方法排程實現程式碼特化(code specialization)的模式。
絕佳的開發者體驗
Skip 的設計初衷是支援絕佳的開發者體驗,實現迅速迭代(常見於動態語言)。編譯器支援增量型別檢查(使用 IDE 外掛的 alpha 版本,該版本可在你輸入時實現接近實時更新),提供常見的語法錯誤提示,幫助新手學習語言、識別方法/類別名的打字錯誤,甚至識別 Skip 標準庫方法名的常見別名,並建議正確的名稱。Skip 還主打程式碼格式化工具(確保一致的程式碼風格)和執行程式碼模組的工具。
初涉 Skip 語言
-
文件地址:http://www.skiplang.com/docs/hello_world.html
-
試驗地址:http://www.skiplang.com/playground/
Hello world!
我們從經典的 Hello world 程式開始。
fun main(): void { print_raw("Hello world!") }
從中可以發現很多東西。函式需要型別註釋:我們指定了返回型別(void)。名為 main 的特殊函式被呼叫作為程式的入口點。我們沒有使用關鍵詞 return,因為 Skip 是一種基於表示式的語言:沒有語句的概念。接下來我們將看到如何在序列中編寫表示式。
函式
使用 fun 來宣告一個函式:
fun add1(x: Int): Int { x + 1 }
Skip 是一種型別化的語言,函式宣告必須包括所有引數的型別以及函式返回的型別。函式的主體是一個表示式,它被評估以產生函式的返回值。
變數通過運算子 = 引入:
fun add1(x: Int): Int { y = 1; x + y }
變數 y 在沒有其指定型別的情況下被引入;區域性變數的型別是被推斷出來的,很少需要明確指定。
上面的例子還引入了;運算子,被其分開的表示式按順序進行評估。
若要修改區域性變數,需在 = 左邊的變數前面加上!。
fun add1(x: Int): Int { y = 0;// declare a local with '=' !y = 1; // modify an existing local by prefixing it with ! x + y }
Skip 是一種型別化語言。函式引數、返回型別和類別欄位等宣告都包括型別註釋。編譯器計算所有表示式的型別,並在遇到預料之外的型別時報錯。Skip 包括常見的基元型別:Int、Float、String、Char、Bool、void。
控制流
Skip 包括常見的控制流語句,如 if、for/in、while、do 和 loop。與大部分語言不同,Skip 的控制流語句是表示式,且和其它表示式一樣可以生成值。控制流表示式可用於期望使用的任何語境。
If-else
if/else 評估兩個可能的表示式中的一個。
fun maybeAdd1(condition: Bool, x: Int): Int { y = (if (condition) 1 else 0); x + y }
在 if 表示式的結果必須是 void 型別表示式的情況下,else 從句可能被省略。
fun maybeAdd1(condition: Bool, x: Int): Int { y = 0; if (condition) { !y = 1 }; x + y }
迴圈
for/in 表示式使得在集合或者序列所有元素上的迭代成為可能。
fun findMax(values: Sequence<Int>): Int { max = Int.min; for (value in values) { if (value > max) { !max = value } }; max }
for/in 表示式主體中的 break 表示式終結了這一迭代。
fun getAge(name: String, people: Sequence<Person>): Int { for (person in people) { if (person.name == name) { break person.age } } else -1 // Return -1 if the person is not found. }
類似地,while 和 do 迴圈可能包括其它子句:
fun getAgeWhile(name: String, people: Sequence<Person>): Int { iter = people.values(); current = iter.next(); while (current.isSome()) { person = current.fromSome(); if (person.name == name) { break person.age } !current = iter.next(); } else -1 // Return -1 if the person is not found. }