iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0

來聊聊 Fennel 的歷史吧,這是個跟創新有關的故事。

從個人到社群:Fennel 的誕生

這邊要談四位重要的人物,沒有他們就不會有這個系列文:

  • Thiago de Arruda

據說 Thiago 有一回想為 Vim 加上非同步與多線程的支援,但礙於 Vim 的專案架構複雜,難以大刀闊斧地修改。他與其他幾位開發者意識到,要徹底解決這個問題,唯一的辦法就是從頭重寫一個新的 Vim,這就是 Neovim 的起源。從 Neovim 的開始,他就引入了 Lua 做為 plugin script。

  • Calvin Rose

Calvin 在某一天覺得如果有個 Lisp 可以跑在 Lua 之上很有趣,於是他就順手刻出了 Fennel 的原型 (prototype),然後就發表它。

  • Phil Hagelberg

Phil 在網路上發現了 Fennel ,然後就當起了 Fennel 的領導開發者 (lead developer),陸續發展了社群、文件、回答問題等諸多工作。現在的 Fennel 社群與官網,都是他在維護。

  • Olical Caldwell

Olical 是一位 Clojure programmer 同時也是一位 Neovim 的使用者。有一天,他決定要用 Clojure 語言發展自己的互動式開發插件 ,這就是 Conjure 的開始。在 Conjure 開始之前,就已經存在其它的 Vim 的互動式開發插件,而這些插件往往是用 VimScript 寫的,所以難讀難改。

接著,轉折點發生了。有一天, Olical 發現了 Fennel 可以編譯成 Lua 之後,他就把本來用 Clojure 寫的 Conjure 移植到 Fennel 之上。這個移植有許多好處,因為一旦移植之後,該插件就不再需要依賴於 JVM ,可以完全跑在 Neovim 裡頭了,這會讓速度大幅地改進。於是,Conjure 就從一個 Clojure 專案,變成了一個 Fennel 專案。

而我是 Conjure 的使用者,因為用了 Conjure 才發現了 Fennel 。

「成功的創新」是對機會的系統性回應

這些故事不僅僅是偶然,更可以看成是對特定機會的系統性回應。杜拉克在《創新與創業精神》一書中,提出了七項紀律來闡述如何有系統地選取創新機會。

  • Thiago 創造 Neovim,是利用了 Vim 程式碼維護困難的意外事件,為「更好的插件開發流程」這一流程需求提供了革命性的解決方案。
  • CalvinPhil 則分別是「Lisp on Lua」這一新知識的創造者與推廣者。Phil 透過建立社群,成功地利用了 Neovim 使用者日漸增加的人口結構變化,將 Fennel 從語言的原型培養成一個活躍的生態系。
  • Olical 移植 Conjure 到 Fennel,是敏銳地捕捉到 Fennel 能編譯成 Lua 這個新知識,並將其應用在產品上,解決了既有的效能瓶頸。

這些人之所以能引發革命,一方面是因為他們具備高超的技術,更重要的是,他們能系統性地識別並利用這些創新機會,將看似偶然的契機,轉化為卓越的軟體與蓬勃的社群。

開發環境 - 安裝

Fennel 的開發環境,主要由以下的幾項東西構成:

  1. Neovim:既是編輯器 (Editor),也是運行環境 (Runtime)。
  2. 語法高亮度插件。
  3. 彩虹漸層色括弧插件。
  4. Conjure:支援互動式開發的插件。
  5. S 表示法編輯 (s-expression editing) 的插件。
  6. 程式碼排版設定。

Neovim

如果你是用 Mac 電腦的話,就用 brew 來安裝吧。

  • 安裝 brew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • 安裝 neovim
brew install neovim

如果你的電腦裡已經有 neovim 了,也別忘了先檢查一下版本。

~$ nvim --version
NVIM v0.11.3

如果不是 >= v0.11.3 版的話,之後遇到一些插件不相容的問題時,也請考慮昇級 Neovim 。

Fnlfmt

Fnlfmt 也是直接安裝在作業系統裡,所以我們用 brew 安裝:

brew install fnlfmt

Neovim 的插件

  • 首先要安裝 Pluggin Manager ,我是用 vim-plug 。

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'
  • 接下來,透過 vim-plug 來安裝插件。

打開你的 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 的檔案,看起來應該會像這樣子:

https://ithelp.ithome.com.tw/upload/images/20250902/20161869Gn9OOvYX27.png

開發環境 - 插件介紹

  • vim-plug

vim-plug 是極簡的插件管理器。現在最流行的插件管理器是 LazyVim ,但是,由於我本人從來也沒有特別覺得我的插件載入速度不夠快,就一直沒有昇級上去。

  • tomasr/molokai

molokai 是配色。這純屬我個人的偏好,就一併放進去了

  • 高亮度相關

fennel.vim 是 Fennel 語言的語法高亮度。
vim-rainbow 則是讓括弧呈現彩虹漸層色。

  • 互動式開發

Conjure 是互動式開發的插件,如果是在 Clojure 語言的時候,它的功能非常多。多到要相當的時間才能精通。另一方面,在 Fennel 語言時,Conjure 也最簡化了,只剩下極簡的核心功能。

互動式開發可以讓你輕易地擷取程式碼語法樹裡的部分子樹,送交給直譯器求值之後,再快速顯示在編輯器裡。

  • S 表示法編輯

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 repo ,並且切換資料夾
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 存檔。

  • 利用 Neovim 的 Ex 指令來求值
:ConjureEval (+ 1 2 3)

注意到了嗎?當你按下 Enter 時,這個 (+ 1 2 3) 已經被交給了一個 Fennel 的 interpreter ,並且對它進行了求值的動作了。

https://ithelp.ithome.com.tw/upload/images/20250902/20161869hUMii9SqiA.png

  • 利用 Neovim 的快捷鍵求值 (eval current-form)
  1. 將游標移到第一行的第一個字元 ( ,然後按下 ,ee
  2. 將游標移到第四行的第一個字元 ( ,然後按下 ,ee

注意到了嗎?當你按下 ,ee 時,游標對應的括弧內容已經被交給了一個 Fennel 的 interpreter ,並且對它進行了求值的動作了。

https://ithelp.ithome.com.tw/upload/images/20250902/20161869UyEy3wlCiD.png

  • 警告訊息

如果你遇到以下的紅字,先忽略即可,你還是可以正常操作的。會有這種紅字,通常是按太快了,它還沒有來得及詢問你,是否願意 trust 某個設置檔。一旦你按了 allow,就不會再有警告。

https://ithelp.ithome.com.tw/upload/images/20250902/20161869r4YQtDIsvi.png

S 表示法編輯

S 表示法編輯有很多的招式,先談談最基本的兩招。

  1. slurp (吞下去)
  2. barf (吐出來)

Slurp

  • 先在 (greet ...) 函數的右邊,增加一個字串 :great 。這邊特別注意一點,在 Fennel 語言,如果是連續字串可以用在它之前加一個冒號的方式來表示。
(greet :world) :great
  • 游標移到 row, column (4, 14) 的位置,並且按下 >)

https://ithelp.ithome.com.tw/upload/images/20250902/20161869EdZPQnhsBq.png

注意到了嗎?當你按下 >) 時,游標對應的括弧就被右移了,而且右移的位置恰好到 :great 的右邊,彷彿就是把 :great 吞下去。

https://ithelp.ithome.com.tw/upload/images/20250902/20161869PrstGZghuH.png

Barf

  • 游標移到 row, column (4, 21) 的位置,並且按下 <)

注意到了嗎?當你按下 <) 時,游標對應的括弧就被左移了,而且左移的位置恰好到 :great 的左邊,彷彿就是把 :great 吐出來。

小提醒

安裝、設定這一步很容易卡關。如果你卡關了,不妨考慮寫信給我?又或是到 Clojure 社群找人問問?

小結

本章談了一些 hacker 故事還有一些 hacker 的技巧:互動式開發S 表達式編輯

某些程度來講,這些人就像是傳說中的 programmer 一樣:徒手刻出一隻編輯器、徒手刻出一隻程式語言、徒手造就一個社群。

你比較想傳頌別人的傳說?還是活在傳說裡?


上一篇
序: AI 加速編碼後,你該學什麼?
下一篇
Fennel 語言速成 -- Lisp 語法
系列文
在 Neovim 中探索 Fennel 與函數式編程4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言