如何評價七牛雲存儲的 qnlang?
qiniu/qnlang · GitHub
又不見你們來幫我改改 gisp2 http://github.com/Dwarfartisan/gisp2 ,這個可是我們已經在實用中的,除了寫的爛點兒還有啥毛病啊你說。謝邀,好心栓 (;′Д`A
槽點過多了,就算原來用 Go 的也會被坑死。
for語句
除了不支持 for range 文法,也不支持中途 continue、break(但是支持 return)。其他和 Go 語言完全類似
外部調用時,一個參數的不同,能改變整個腳本的語義在一個細節上 qnlang 的 defer 和 Go 語言處理並不一致,那就是 defer 表達式中的變數值。在 Go 語言中,所有 defer 引用的變數均在 defer 語句時刻固定下來(如上面的 f 變數),後面任何修改均不影響 defer 語句的行為。但 qnlang 是會受到影響的。例如,假設你在 defer 之後,調用 f = nil 把 f 變數改為 nil,那麼後面執行 f.close() 時就會 panic。
javascript 能夠自動插入分號,已經有兩個派別了C原教旨分號派無分號派(可惜做不到,只能盡量少的人工插入)雖然曾經吵的不可開交,但是還好,這兩種相互調用是沒有問題的。在大部分正式的場合,qnlang.New 傳入 qnlang.InsertSemis 會更多一些。它表示在各行的末尾智能的插入 ";"。但是在本例中我們希望執行的是一個表達式,而不是語句,所以傳入 nil 參數更為合適。如果我們改為傳入 qnlang.InsertSemis,那麼得到的結果就不再是 3,而是 nil。因為表達式 1+2 的結果是 3,但是表達式 1+2; 的結果是 nil。
lang, err := qnlang.New(nil) // 參數 nil 也可以改為 qnlang.InsertSemis
err = lang.SafeEval(`1 + 2`)
qnlang 還提供自動插入分號的開關,那麼會有三個派別
C原教旨分號派 (可以有 InsertSemis )C原教旨分號派 (無 InsertSemis )無分號派(必須有 InsertSemis )無 InsertSemis 是沒辦法調用 無分號派 的庫文件的。一個小眾語言,開始就分裂了兩種方言,而且相互調用都有問題。這能發展?為了省略一個定義局部變數的 var 或者 local,搞出下面這兩個奇怪的東西。這樣應該是只能有函數級作用域而沒有塊級作用域的吧。命名衝突非常多,凡是閉包使用的變數,在函數調用鏈上都不能重名。如果你想修改外層變數,需要先引用它,如下:
x = fn(a) {
b = 1
y = fn(t) {
b; b = t // 現在正常了,我們知道你要修改外層的 b 變數
}
y(a)
return b
}
私有變數
在 qnlang 中,我們引入了私有變數的概念。
私有變數以 _ 開頭來標識。私有變數只能在本函數內使用,並且互不干擾(沒有名字衝突)。如:
x = fn(a) {
_b = 1
y = fn(t) {
_b = t
}
y(a)
return _b
}println(x(3)) // 輸出 1,因為 y(a) 這個調用並不影響 x 函數中的 _b 變數
推薦最近的一篇博文:http://www.unofficialgoogledatascience.com/2015/12/replacing-sawzall-case-study-in-domain.html Replacing Sawzall — a case study in domain-specific language migration
大意就是google用go替代了一個日誌處理的DSL sawzall,其中有段話:There』s a natural tension for any domain-specific language between staying highly focused on its problem domain and growing to accommodate the needs of users who want to stretch the language in new directions. Sawzall』s development was shaped by this tension from the very beginning — early designs didn』t even include the ability to define functions, but functions were quickly added when it became apparent that the language couldn』t meet users』 needs without them. Over time, many more features have been added. But as the language grows, the rationale for using a domain specific language rather than a general purpose language becomes more and more diluted.
說不定以後也會像google數據處理團隊這樣,從DSL遷移回一門通用語言,所以何不現在就放棄這條路線呢。
一些使用DSL的理由,已經是不成立,或者將要不成立。例如熱更新,如果實現了plugin機制[1],那就沒必要用DSL做了。[1] https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1 ,最近幾個版本都在推進度仔細想了幾天,反正就算我投簡歷也會被拒,那我放下包袱說實話好了,不怕得罪人了。
從 spec 來看這個語言比 golang 還更具 golang 精神,如果一切如文檔所說那的確是非常了不起的語言,近二十年流行的語言中,的確少見這樣的。
一切就看大會的時候 qnlang 能否演示出文檔所說的功能了
許式偉在微信群裡面說了,他是為了完成鄒欣的一個給大學生的作業才搞了這個東西,結果不斷地加啊加啊,就變成一門語言了。而且你看那廣告,就是在往go的冷屁股上貼啊。所以你們不要想太多。
而且動態強類型的意思就是說,把本來編譯器應該做的事情,都交給runtime去做了……既沒有動態語言的靈活,也沒有靜態語言的安全,什麼都沒有了。的確是做作業的追求。千萬不要給王垠看到
沒有看到具體的Quality,Performance,Spec,光看這個頁面介紹,本科畢設水平吧。
牛逼的人已經寫出一門語言,而吐槽的人卻還在找工作……
雖然 qnlang 看起來像動態語言,但其實它是強類型的語言。
雖然?但是?
外行
這要是當大作業交上來,還排不上前三(逃)
利益相關:剛主持完作業展示會的逗比TAqnlang只是golang版本的tpl(text processing language)庫的一個demo,花了7個晚上搞成,沒有什麼驚人的特性,實在難入大家的法眼,不好意思。有本事就自己擼一個給我漲漲姿勢唄,光噴於整個社會進步又有什麼鳥用。
另外,1月(下個月)22、23號將於北京舉行的ecug大會上,我會介紹這門語言是如何構建出來的。整個演講會圍繞如何用qnlang製作一個支持常規四則運算和函數的計算器(大約100行代碼)以及qnlang本身(也就是所謂的自舉,大約1300行代碼)來講解。這個qnlang版本的計算器和qnlang版本的qnlang會在本次ecug大會後放出。
如果你想做為聽眾參加ecug大會,或者希望做為講師與大家交流你的技術心得,請訪問 http://www.ecug.org 了解詳情(不能訪問請自備梯子)。
----
12.12日更新:今晚抽空寫完了qnlang自舉的代碼,實際代碼比golang版本少一些,只有900行。詳見:- qiniu/qnlang · GitHub
這裡可下載 qnlang,並且提供了多個樣例。ecug 大會上會講解背後的工作原理。
說真的,動態強類型哭給你看啊

必須 at一下 @vczh 還有 @曾博
------------------------------致在評論里這些排隊的壞人.................你們啊,唉,說什麼好呢,也不點個贊。就那語言的介紹頁面,點開都不想繼續往下看了,就算是程序員,也是有審美的呀!
我只是想吐槽這個名字,拼了半天,七牛郎?!
這兩天有打算用用qnlang,結果發現知乎里在講這個。樓上不少人評論說是大作業水平,我仔細讀了一下,才發現評論本身許多也不過就是大作業。先贊一個viruscamp bhuztez 的幾點分析。結論不敢苟同,但分析對象總還是打在點上。
先說說我同意的部分:

go語言的霸主地位……
這個我就不說什麼了。很多語言都這麼講過,又很多語言的愛好者都這麼講過。但還是有更多的「很多很多語言」冒出來,然後繼續講相同的話。比如PHP無所不能~~Perl比無所不能還要好一點的是能寫出PHP~~但在正式的文檔里用這樣的措辭,多少看起來也是不正式的。
接下來講講正經的。1、其實這裡沒分清楚語句還是表達式需要注意的是,if 語句是有值的。比如:
x = if a &< b { a } else { b } // x 取 a 和 b 兩者中的小值。即 x = min(a, b)如果你希望使用 if 表達式的值,建議不要寫成多行:
x = if a &< b { a } else { b }這段代碼不會如你所願工作。原因是編譯器會在行末自動插入 ";",所以相當於是:
x = if a &< b { a; } else { b; }結果是 x = nil。
這個真的有很大問題么?寫文檔的人大概真是沒寫清楚,或者原本也沒有想得很清楚。
在x = if a &< b { a } else { b }
這個裡面,if其實不能理解為「if語句」,因為語言層面上來講,=運算符後面就應該是表達式求值,而不應該是語句,否則就沒法做parser了。那麼,你如果把這裡的if直接理解成「if表達式」呢?那就完全不違和了。簡單地說,上面的一行代碼是相當於:
哪就很好嘛,既然js的三元表達式能寫成?:,為什麼qnlang的「if表達式」就不寫成上面這樣呢?同樣的,既然是「表達式」,要求{a} 裡面不能出現「語句結束符」,也是正常的邏輯。x = a&
所以對照來看,qnlang在文檔裡面,錯誤的把這裡的if解釋成了「有返回值的if語句」。正確的說法,可以是「這裡是一個if表達式」。你看後面,文檔又寫成了「如果你希望使用if表達式的值……」,喏,又繞回來了。x = (a&
x = fn(a) {
b = 1
y = fn(t) {
b; b = t // 現在正常了,我們知道你要修改外層的 b 變數
}
y(a)
return b
}
println(x(3)) // 輸出 3
這個,如果b=1正確,那後面b=t就不應該拋異常。因為如果你承認
未聲明local b,則訪問upvalue」
那麼這兩處就都該正確;如果你不承認,那麼這兩處都應該報「變數b未聲明」。
你不能在相同的scope中,對相同的代碼使用不同的語義,這是錯誤的設計。至於還非要用「b;」來表明引用upvalue,坦率地說,得不償失。6、私有變數以 _ 開頭來標識。私有變數只能在本函數內使用,並且互不干擾(沒有名字衝突)
這種「名字魔法」是典型的歷史遺留風格/習慣,建議放棄。如果這是合理的,那麼將來就會設計出$前綴的意義、*前綴的意義,以及%前綴的意義,然後代碼的可讀性就直落千丈。
在語言設計上,應該盡量減少這類「把使用習慣當語法風格」來約定的spec。另外,我完全看不出這裡省略var聲明的必要性,也看不出它與lua local聲明(局部變數)之間有任何的區別。7、外部調用時,一個參數的不同,能改變整個腳本的語義這個沒問題。就是你「初始化一個怎樣的引擎」而已。這個在嵌入中還是有用的,畢竟基於表達式解析和基於語句解析是有不同的。最後,正如qnlang所講的,我看重的正是它在嵌入go上面的能力,對go原生runtime/module支持的能力等等。我想,僅僅因為一些語法語義上的分歧,而不能觀見到這個語言的好的方面,以及在這些好的方面的精巧實現,對大多數人來說,是丟了西瓜撿了芝麻的。而在七牛方面來說,qnlang發布一個可用的release是可以的,但spec大概只能出到0.9 alpha。有些問題沒解釋清楚,先行應用是可以的,但當成語言規範發布則過於冒失了。若有開放的心態,把語言規範草案寫出來放到社區討論,謹慎修訂並apply到新的qnlang版本,也是對這門語言的促進。沒用過,但感覺在 go 語言上再搞個語言干腳本的工作,有這個精力,為什麼不把 v8 和 node.js 的源代碼拿過來,把 Ruby 那套語法邏輯弄進去?Go 語言畢竟不像 v8 那種虛擬機那麼靈活又強大
推薦閱讀:
※actor模型除了erlang和skynet用得廣泛嗎?
※如何用C語言實現異常/狀況處理機制?
※Lua 語言有哪些不足?
※為什麼Lua不支持大多數編程語言都有的continue,卻非得支持一般情況下用得很少的 repeat until ?
※為什麼很多編程語言用 end 作為區塊結束符,而放棄花括弧?
