我最近需要為製造領域設計一個新的參考資料系統。在初期設計階段,我們提出使用 Clojure 程式語言的想法。雖然一開始我對偏離標準開發堆疊持懷疑態度,但我逐漸意識到利用這門語言所帶來的多重機會的價值。

這篇部落格文章是我對 Clojure 程式語言及其導入開發堆疊的高層次看法。我將介紹促使我選擇它而非更傳統語言如 Java 的幾個關鍵點。

Clojure 是一種動態函數式程式語言,運行於 Java 虛擬機(JVM)上,屬於 Lisp 家族(沒錯,就是那個非常古老的語言)。

因此,Clojure 不僅享有函數式程式語言著名的優點,如不可變資料結構(本文不詳述),還具備 Lisp 特有的優勢,例如程式碼即資料(code-as-data)的語意。

此外,Clojure 生態系統提供了豐富且強大的資料操作函式庫與工具,使其非常適合用於快速原型開發複雜的資料驗證與轉換程式。

Clojure 並非新興語言,誕生於 2007 年,但多年來多被視為「業餘愛好者」語言,僅用於特定場合,儘管其初衷是成為多用途且穩定的商業用語言。

然而,近年來 Clojure 在企業界的使用顯著增加。以 ThoughtWorks Radar 為例,自 2014 年起即將 Clojure 列為「採用」階段,代表它已是企業級成熟技術。

決策的第一個因素是,我們的產品涉及大量資料結構與業務管理規則,這些規則需要頻繁演進並適應新的商業情境。

因此,我們想到使用文字描述語言(DSL,領域特定語言)來指定業務邏輯,而非「硬編碼」的程式碼。傳統物件導向語言似乎不適合此需求,而 Clojure 則天生具備所需功能,或可透過強大函式庫如 malli、specter 實現。

另一方面,我必須評估某些業務案例的複雜度,並在開發早期證明可行性。Clojure 工具箱提供了便捷的資料探索方式,使我們能在極短時間內開發原型;若使用其他語言,則需撰寫大量樣板程式碼。

Clojure 最大的優勢之一來自其 Lisp 本質。Clojure 使用 EDN(Extensible Notation Format)的超集,允許以易讀的標記格式宣告程式碼。

因此,Clojure 非常適合創建豐富的 DSL,且能無縫整合進語言本身。你可能聽過 Lisp 自豪地宣稱程式碼即資料,資料即程式碼。

許多 Lisp 特性已被其他語言借鑑,但 Lisp 的程式碼即資料觀念及巨集系統仍獨樹一格。

我所參與的軟體產品需要實作一種輕量級語言,以宣告式方式表達業務邏輯,類似規則引擎。這個需求透過單一 Clojure 變數完成。

目標是使用簡單且精煉的資料結構,方便非開發者修改並存入資料庫。但 Clojure 也提供巨集系統,能實作更複雜的 DSL。

另一個讓我選擇 Clojure 的關鍵特性是其 REPL(Read-Eval-Print Loop)環境,允許程式設計師與執行中的程式互動,逐條評估程式碼並即時修改。

REPL 是 Lisp 程式設計師的秘密武器,能讓你與程式互動並快速演進。Clojure 社群中關於 REPL 驅動開發的討論從未停止,且相當盛行。原因在於它能讓你高效且迅速地撰寫程式,找錯或增刪功能都能透過快速回饋輕鬆完成。

快速迭代開發與測試的能力,在初期探索階段幫助極大。開發者能在工作坊中即時嘗試新程式碼片段,大幅提升效率。正如 Clojure 文件所述:

許多 Clojure 程式設計師認為 REPL 及其緊密的回饋迴路,是使用 Clojure 最具吸引力的理由。這並不代表 Clojure 的語言特性,如不可變資料結構,沒有價值:REPL 之所以強大,正是因為這些特性,尤其是 Clojure 是為互動式開發而設計。

此外,REPL 在 IDE 插件如 cursive 或 calva 中的整合,讓互動性更進一步,能按需執行小段程式碼甚至單一表達式。

由於 Clojure 運行於 JVM,能存取所有 Java 函式庫及框架,並可在 Clojure 與 Java 之間互調程式碼。

在我的情境中,這是巨大優勢,有助於將 Clojure 無縫整合進我們的標準開發堆疊:Java/SpringBoot。

儘管互操作性降低了進入門檻,但關鍵決策是使用深度。

我們考慮多種選項,越複雜的功能需要越高的經驗。我的挑戰是找到短期生產力與中期技能管理風險間的平衡。

我最終決定循序漸進,先將 Clojure 用於原型開發,再逐步將功能與模組整合進產品程式碼。

如此,我們成功加入多項直接利用程式碼即資料語意與 REPL 的功能,同時控制技術方案整體複雜度。這也讓開發團隊隨時間提升 Clojure 技能。

以下是我考慮未來階段採用的額外功能列表:

巨集系統:允許使用者程式碼擴展編譯器。巨集可定義其他語言需內建支援的語法結構,適合我們自訂 DSL 引擎的挑戰。

ClojureScript:Clojure 的 JavaScript 編譯器,可產生瀏覽器端執行的程式碼,縮短前後端技術差距。

資料友好函式庫:Clojure 生態系統提供多種函式庫,如 ClojureSpec、Schema、Malli,對我們相當有用。

決定 Clojure 使用深度時,必須考慮人力配置。Clojure 的函數式與資料導向方法,與許多傳統程式設計觀念相悖。對只熟悉物件導向的團隊,學習曲線可能陡峭。市場上具經驗的 Clojure 開發者也相對稀少。

因此,建議採取非常漸進的方式,逐步利用上述優點,一步步推進。

「移山者,始於挖小石。」

對我個人而言,這種方法證明值得,因為它讓團隊從專案初期就能有效率,並隨著進展深化技術應用。

至今,我持續評估在軟體架構其他部分利用 Clojure 的機會,同時致力於提升並維持團隊的高水準能力。

總結來說,以下是讓 Clojure 成為首選的理由。

話雖如此,對於需要快速資料驅動開發的新專案,值得嚴肅考慮嘗試 Clojure。