來聊聊 Fennel 的歷史吧,這是個跟創新有關的故事。
這邊要談四位重要的人物,沒有他們就不會有這個系列文:
據說 Thiago 有一回想為 Vim 加上非同步與多線程的支援,但礙於 Vim 的專案架構複雜,難以大刀闊斧地修改。他與其他幾位開發者意識到,要徹底解決這個問題,唯一的辦法就是從頭重寫一個新的 Vim,這就是 Neovim 的起源。從 Neovim 的開始,他就引入了 Lua 做為 plugin script。
Calvin 在某一天覺得如果有個 Lisp 可以跑在 Lua 之上很有趣,於是他就順手刻出了 Fennel 的原型 (prototype),然後就發表它。
Phil 在網路上發現了 Fennel ,然後就當起了 Fennel 的領導開發者 (lead developer),陸續發展了社群、文件、回答問題等諸多工作。現在的 Fennel 社群與官網,都是他在維護。
Olical 是一位 Clojure programmer 同時也是一位 Neovim 的使用者。有一天,他決定要用 Clojure 語言發展自己的互動式開發插件 ,這就是 Conjure 的開始。在 Conjure 開始之前,就已經存在其它的 Vim 的互動式開發插件,而這些插件往往是用 VimScript 寫的,所以難讀難改。
接著,轉折點發生了。有一天, Olical 發現了 Fennel 可以編譯成 Lua 之後,他就把本來用 Clojure 寫的 Conjure 移植到 Fennel 之上。這個移植有許多好處,因為一旦移植之後,該插件就不再需要依賴於 JVM ,可以完全跑在 Neovim 裡頭了,這會讓速度大幅地改進。於是,Conjure 就從一個 Clojure 專案,變成了一個 Fennel 專案。
而我是 Conjure 的使用者,因為用了 Conjure 才發現了 Fennel 。
這些故事不僅僅是偶然,更可以看成是對特定機會的系統性回應。杜拉克在《創新與創業精神》一書中,提出了七項紀律來闡述如何有系統地選取創新機會。
這些人之所以能引發革命,一方面是因為他們具備高超的技術,更重要的是,他們能系統性地識別並利用這些創新機會,將看似偶然的契機,轉化為卓越的軟體與蓬勃的社群。
Fennel 的開發環境,主要由以下的幾項東西構成:
如果你是用 Mac 電腦的話,就用 brew 來安裝吧。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install neovim
如果你的電腦裡已經有 neovim 了,也別忘了先檢查一下版本。
~$ nvim --version
NVIM v0.11.3
如果不是 >= v0.11.3
版的話,之後遇到一些插件不相容的問題時,也請考慮昇級 Neovim 。
Fnlfmt 也是直接安裝在作業系統裡,所以我們用 brew 安裝:
brew install fnlfmt
vim-plug 安裝:
sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
打開你的 Neovim 設定檔:
~$ nvim $HOME/.config/nvim/init.vim
在該檔案裡,貼上下方的內容:
" Specify a directory for plugins.
call plug#begin(stdpath('data') . '/plugged')
" === Basic Setup ===
" color, look and feel
Plug 'tomasr/molokai'
" === Highlight ===
" Syntax highlight for fennel language
Plug 'bakpakin/fennel.vim'
" rainbow parentheses
Plug 'frazrepo/vim-rainbow'
" === Interactive Development ===
" Conjure
Plug 'Olical/conjure'
" === S-expression editing ===
Plug 'guns/vim-sexp'
Plug 'tpope/vim-sexp-mappings-for-regular-people'
Plug 'kylechui/nvim-surround'
Plug 'jiangmiao/auto-pairs', { 'tag': 'v2.0.0' }
call plug#end()
colorscheme molokai
" Config the rainbow-parentheses
let g:rainbow_ctermfgs = [
\ 'red',
\ 'yellow',
\ 'green',
\ 'cyan',
\ 'magenta',
\ 'gray'
\ ]
" rainbow parentheses enabled for fennel
autocmd FileType fennel call rainbow#load()
let maplocalleader=","
let mapleader="\\"
" Make vim-sexp recognizes the fennel
let g:sexp_filetypes = 'fennel'
lua require("nvim-surround").setup()
function! Fnlfmt()
!fnlfmt --fix %
" :e is to force reload the file after it got formatted.
:e
endfunction
augroup FennelOnSave
" Format after save
autocmd!
autocmd BufWritePost *.fnl call Fnlfmt()
augroup END
接著,在 Neovim 的 Normal Mode 裡,執行以下的指令:
:source %
:PlugInstall
安裝完成之後,開啟一個 Fennel 的檔案,看起來應該會像這樣子:
vim-plug 是極簡的插件管理器。現在最流行的插件管理器是 LazyVim ,但是,由於我本人從來也沒有特別覺得我的插件載入速度不夠快,就一直沒有昇級上去。
molokai 是配色。這純屬我個人的偏好,就一併放進去了
fennel.vim 是 Fennel 語言的語法高亮度。
vim-rainbow 則是讓括弧呈現彩虹漸層色。
Conjure 是互動式開發的插件,如果是在 Clojure 語言的時候,它的功能非常多。多到要相當的時間才能精通。另一方面,在 Fennel 語言時,Conjure 也最簡化了,只剩下極簡的核心功能。
互動式開發可以讓你輕易地擷取程式碼語法樹裡的部分子樹,送交給直譯器求值之後,再快速顯示在編輯器裡。
S 表示法是指,一個函數的寫法是把函數名稱放在括弧之內的寫法。
;; S expression
(fn your-fn-name [arg1 arg2]
...
)
;; M expression
fn your-fn-name [arg1 arg2] (
...
)
S 表示法編輯,這個也跟互動式開發一樣,是 95% 的 programmer 聞所未聞的概念。它可以讓你在程式語言的語法樹上,透過指令進行編輯,也就是說,你編輯的最小單位,不再是一個又一個的字元 (char)、字 (word)、行 (line) ,而是語法樹 (syntax tree) 裡的元素 (element)。
以下這一段 VimScript 程式碼,它的功能就是讓你撰寫的 Fennel 程式碼在存檔完之後,就會自動觸發 fnlfmt
這隻程式來做自動排版。
function! Fnlfmt()
!fnlfmt --fix %
" :e is to force reload the file after it got formatted.
:e
endfunction
augroup FennelOnSave
" Format after save
autocmd!
autocmd BufWritePost *.fnl call Fnlfmt()
augroup END
git clone https://github.com/humorless/auto-conjure.git
cd auto-conjure
nvim fnl/auto-conjure/hello.fnl
並且貼上以下的內容:
(fn greet [name]
(.. "Hello, " name "!"))
(greet :world)
下指令 :w
存檔。
:ConjureEval (+ 1 2 3)
注意到了嗎?當你按下 Enter 時,這個 (+ 1 2 3)
已經被交給了一個 Fennel 的 interpreter ,並且對它進行了求值的動作了。
(
,然後按下 ,ee
(
,然後按下 ,ee
注意到了嗎?當你按下 ,ee
時,游標對應的括弧內容已經被交給了一個 Fennel 的 interpreter ,並且對它進行了求值的動作了。
如果你遇到以下的紅字,先忽略即可,你還是可以正常操作的。會有這種紅字,通常是按太快了,它還沒有來得及詢問你,是否願意 trust 某個設置檔。一旦你按了 allow,就不會再有警告。
S 表示法編輯有很多的招式,先談談最基本的兩招。
(greet ...)
函數的右邊,增加一個字串 :great
。這邊特別注意一點,在 Fennel 語言,如果是連續字串可以用在它之前加一個冒號的方式來表示。(greet :world) :great
>)
注意到了嗎?當你按下 >)
時,游標對應的括弧就被右移了,而且右移的位置恰好到 :great
的右邊,彷彿就是把 :great
吞下去。
<)
注意到了嗎?當你按下 <)
時,游標對應的括弧就被左移了,而且左移的位置恰好到 :great
的左邊,彷彿就是把 :great
吐出來。
安裝、設定這一步很容易卡關。如果你卡關了,不妨考慮寫信給我?又或是到 Clojure 社群找人問問?
本章談了一些 hacker 故事還有一些 hacker 的技巧:互動式開發與 S 表達式編輯。
某些程度來講,這些人就像是傳說中的 programmer 一樣:徒手刻出一隻編輯器、徒手刻出一隻程式語言、徒手造就一個社群。
你比較想傳頌別人的傳說?還是活在傳說裡?