Tuesday, February 21, 2017

clojure vim 開發環境設置 --- rainbow-parentheses, cljfmt

clojure 搭配 vim 的開發環境設置中,有一項特別的 syntax highlight  ,算是其它語言比較不需要,但是 lisp 家族的語言特別需要的特色:彩虹括號 rainbow parentheses



安裝了這個 vim 的 plugin 之後,寫出的 clojure 的程式碼,插號就會由內而外地照彩虹來變色。而且連中括號的顏色也可以一起變化。




安裝 vim 的 rainbow parentheses
cd ~/.vim/bundle && git clone https://github.com/kien/rainbow_parentheses.vim.git

然後,在 ~/.vimrc 檔內,加入所需要的設置:
let g:rbpt_colorpairs = [
    \ ['brown',       'RoyalBlue3'],
    \ ['Darkblue',    'SeaGreen3'],
    \ ['darkgray',    'DarkOrchid3'],
    \ ['darkgreen',   'firebrick3'],
    \ ['darkcyan',    'RoyalBlue3'],
    \ ['darkred',     'SeaGreen3'],
    \ ['darkmagenta', 'DarkOrchid3'],
    \ ['brown',       'firebrick3'],
    \ ['gray',        'RoyalBlue3'],
    \ ['black',       'SeaGreen3'],
    \ ['darkmagenta', 'DarkOrchid3'],
    \ ['Darkblue',    'firebrick3'],
    \ ['darkgreen',   'RoyalBlue3'],
    \ ['darkcyan',    'SeaGreen3'],
    \ ['darkred',     'DarkOrchid3'],
    \ ['red',         'firebrick3'],
    \ ]

let g:rbpt_max = 16
let g:rbpt_loadcmd_toggle = 0
au VimEnter * RainbowParenthesesToggle
au Syntax * RainbowParenthesesLoadRound
au Syntax * RainbowParenthesesLoadSquare
au Syntax * RainbowParenthesesLoadBraces

另一方面,要人工處理程式碼的對齊,其實也是很麻煩的事。寫過 golang 用過 gofmt 之後,寫新的語言也會不自覺開始找對應的東西。所幸, clojure 也有 cljfmt 。  

安裝方式:
  1. 安裝 vim-fireplace
  2. 安裝 cljfmt 
  3. 安裝 vim-cljfmt 
想要在 vim 內打 :Cljfmt 就可以自動排版、關閉檔案存檔時也可以自動排版的設置,必須在  ~/.lein/profiles.clj  寫入
:dependencies [[cljfmt "0.5.6"]]
此外,必須是在 leiningen  「使用者域」的設置檔已經寫入之後,才啟動 leiningen repl 。因為不同於 gofmt 是獨立的程式,cljfmt 是需要透過 clojure 的 REPL 來啟動的。

Monday, February 20, 2017

clojure vim 開發環境設置 --- 整合 REPL

開發 clojure ,不免要做的事情自然是不停地調用 REPL 來測試自己寫的函數是否如預期般地執行,而 vim 可以安裝 plugin ,讓這個調用 REPL 的步驟完全留在編輯器內執行。這個開發方式又稱為 live development 。

要設置好這個開發環境,首先要準備幾樣東西:
  1. clojure 的 nREPL
  2. vim 8
在 vim 裡安裝合適的 plugin :
cd ~/.vim/bundle
git clone git://github.com/tpope/vim-fireplace.git
啟用方式:
  1. 在 clojure 的專案資料夾,開啟一個 shell ,啟動 nREPL。指令是 lein repl
  2. 在同一個資料夾,開啟另一個 shell ,啟動 vim 。如此一來, vim 的 fireplace plugin 就會自動與 nREPL 連接了。
文字檔不在 clojure 專案資料夾內時,fireplace 的啟用方式:
  1. 還是一樣啟動 nREPL。指令是 lein repl
  2. 如果文字檔並不是 clj 結尾。需要先讓 vim 知道它是 clojure 的 source code ,下指令
     :set filetype=clojure
  3. 連線到現在已經啟動的 nREP  ,例如下指令
     :Connect nrepl://127.0.0.1:41767
fireplace 的常用指令如下:
求值
  1. 隨便選一個 expression 做 repl     移動標到程式碼上,然後  :Eval 。快速鍵是 cpp 
  2. 指定一段區間的程式碼做 repl      :1,3Eval
  3. 啟動 Clojure Quasi-REPL        cqq
  4. 只打開 Clojure 的 Quasi-REPL   cqc 
在 quickfix 視窗顯示 docstring 或是 source code 
  1. 快捷鍵  K   。        對應 repl 的 (doc ) ,   也可以用 :Doc
  2. 快捷鍵  [d  。        對應 repl 的 (source ), 也可以用 :Source 
在文件裡穿梭 
    1. 快捷鍵  [ ctrl + d  。 跳躍到符號定義的那一行,即使是定義在 jar 檔中。
    2. 快捷鍵  gf  。         語義是 go to file 。 對於 namespace 會產生作用。
    重新載入程式碼
    1. 讓 REPL 重新載入現在的 namespace                                         :Require
    2. 讓 REPL 重新載入現在的 namespace 及其使用的 namespace        :Require!
    程式碼提示 (Omnicomplete)      <C-x><C-o>

    啟動 ClojureScript    :Piggieback [build-id]   // 此處的 build-id 是 keyword 形式
    啟動 ClojureScript 的 REPL 之後,要先
    1.  npx shadow-cljs watch app
    2. 開啟 browser

    Wednesday, February 15, 2017

    推荐 clojure 語言 --- simple, concise, consistent

    最初是看了 Paul Graham 推荐 Lisp ,然後工作上因為 Riemann monitoring tool 是用 clojure (Lisp 的一種 dialect) 寫的,一時興起就決定來研究這個傳說中「駭客的秘密武器」的語言家族。學習的過程就是重複、交替做三件事:
    1. 讀 clojure 的線上書籍、文件,寫語言的特性、語言的觀念 ---重點關注在 clojure 的文章。
    2. 寫一些不痛不癢的小習題。 4clojure.com 有 150 多題的練習題。
    3. 讀一些和 clojure 相關、又不直接談 clojure 的文章。比方說「教 Lisp 的文章」「Lisp 的歷史」「Rich Hickey 的一些 slide 」
    後來研究了所謂的 language oriented programming, bottom-up approach 等等應用 macro 來做 meta-programming 的高級應用技巧。 當我和朋友聊這個的時候,朋友給了一個回應:「嗯,也許 Lisp 語言真的是最適合做 meta-programming 的語言,但是你上一回寫 meta-programming 是什麼時候?」

    於是,我心裡的大哉問就變成了:「對於自身不太使用 meta-programming 技巧的程式設計師,clojure 語言是否依然可以帶來生產力的提昇?」會問這個問題是因為之前有看過一篇文章講為什麼「函數式編程」可以提高程式設計師的生產力,文章中所論述最重要的理由,因為「高階函數」與「惰性求值」這兩項特性可以提高程式碼模塊化的程度。函數式編程的特性,現在許多的語言都加入了,那 clojure 如果不討論 meta-programming 的話,它的表達能力 (expressiveness) 還會比一些更主流的語言 python, Ruby 等也有函數編程特性的語言來得更強嗎?

    那我們來看一段程式碼吧: flatten 函數,可以用來「壓平」樹狀資料結構。
    例如:
    (flatten [1 2 [3 [4 5] 6]])
    (1 2 3 4 5 6)
    接下來,有趣的事就是,如果我們去看 flatten 的 source code 。

    啥! flatten 居然是用 tree-seq 來定義的!為了這件事,我還多看了一下,clojure 哪些內建的函數也是用 tree-seq 來定義? 結果 file-seq 和 xml-seq 也都是用 tree-seq 來定義。而 tree-seq 是一個高階函數,接受三個參數:一個是用來接收「定義分支的函數」、一個是用來接收「展開分支子樹的函數」,一個是用來接收「樹資料結構」。

    「樹」這樣子的資料結構、對應的「走訪方式」,例如: tree-seq。在其它的語言,往往沒有「極簡」的實現。像 python 的話,自然也會有「樹」相關的走訪函式庫、「樹」的資料結構實現。卻比較多是物件導向式的、或是每一家發展不同風格的作法。這一類的問題:標準函式庫較為一致、外部函式庫較不一致的問題,我認為是語言表達能力受限制的主因之一。

    要深入討論語言表現能力的話,我認為這關系到程式語言設計的一致性 (consistency)。以 python 為例, python 在函數式編程領域,後期發展的語法傾向是用 list comprehension 來取代 filter, map, reduce 之類的函數。這個很自然,因為用 list comprehension 的表現能力更強,寫 python 的使用者可以在記憶最少的語法的前提之下,得到最大的表達能力。

    於是這就回到了一個很有趣的議題,程式語言的設計,其實必須考慮人類認知能力的限制。以 C++ 語言為例,C 的語法、語件導向的語法、template 的語法,三種語法都大不相同。tempalte 尤其複雜。複雜的語法導致了使用者學習時難學、使用者上手之後也難用,因為語法冗長。而 Lisp 的括號語法,最初來自於偷懶,因為 Lisp 的作者還沒有為它設計合適的語法,所以就先用語法樹 (syntax tree) 來湊合著用,卻因此讓 Lisp 語言成為了有最簡單 (simple) 語法的語言,同時也是語法最一致 (consistent)的語言,進而可以讓使用者寫出簡潔 (concise) 的程式。