$ clj
Clojure 1.11.2
user=>
Clojure 提供命令列工具,用於
執行互動式 REPL (讀取-評估-列印迴圈)
執行 Clojure 程式
評估 Clojure 表達式
在以上所有情況中,你可能想使用其他 Clojure 和 Java 函式庫 (相依性或「deps」)。這些可能是你正在本地撰寫的函式庫、git 中的專案 (例如在 GitHub 上) 或通常是 Maven 生態系統中可用的函式庫,並由 Maven Central 或 Clojars 等中央儲存庫所主機。
在所有情況下,使用函式庫都涉及
指定你想要使用的函式庫,提供其名稱和其他方面,例如版本
從 git 或 maven 儲存庫取得 (一次) 到你的本機電腦
讓它在 JVM 類別路徑上可用,以便 Clojure 可以在 REPL 或程式執行時找到它
Clojure 工具為 (a) 指定語法和檔案 (deps.edn
),給定這些工具,它們將自動處理 (b) 和 (c)。
請參閱 入門,以取得如何安裝工具的詳細資訊。在此,我們將示範如何開始。請參閱 Clojure CLI 和 deps.edn 以取得完整參考。請參閱 變更日誌 以取得版本資訊。
下載並安裝工具後,你可以透過執行 clj
工具來啟動 REPL
$ clj
Clojure 1.11.2
user=>
進入 REPL 後,你可以輸入 Clojure 表達式並按 Enter 鍵來評估它們。輸入 Control-D 退出 REPL
user=> (+ 2 3) # press the `enter` key after typing the expression to evaluate it
5 # result of expression
user=> # type Ctrl-D here to exit the REPL (does not print)
$
有許多 Clojure 和 Java 函式庫可用,可存取你可能需要的任何功能。例如,考慮常用的 Clojure 函式庫 clojure.java-time,用於處理日期和時間。
若要使用此函式庫,你需要將它宣告為相依性,以便工具可以確保已下載它並將它新增到類別路徑。大多數專案中的自述檔會顯示要使用的名稱和版本。建立一個 deps.edn
檔案來宣告相依性
{:deps
{clojure.java-time/clojure.java-time {:mvn/version "1.1.0"}}}
或者,如果你不知道版本,你可以使用 find-versions
工具,它會列出所有可用的座標,並按順序排列
$ clj -X:deps find-versions :lib clojure.java-time/clojure.java-time
...omitted
{:mvn/version "1.0.0"}
{:mvn/version "1.1.0"}
使用 clj
工具重新啟動 REPL
$ clj
Downloading: clojure/java-time/clojure.java-time/1.1.0/clojure.java-time-1.1.0.pom from clojars
Downloading: clojure/java-time/clojure.java-time/1.1.0/clojure.java-time-1.1.0.jar from clojars
Clojure 1.11.2
user=> (require '[java-time.api :as t])
nil
user=> (str (t/instant))
"2022-10-07T16:06:50.067221Z"
第一次使用相依性時,您會看到關於下載函式庫的訊息。檔案下載後(通常會下載到 ~/.m2
或 ~/.gitlibs
),未來會重複使用。您可以使用相同的程序將其他函式庫新增到您的 deps.edn
檔案,並探索 Clojure 或 Java 函式庫。
很快地,您會想要建立並儲存自己的程式碼,以使用這些函式庫。建立一個新目錄,並將此 deps.edn
複製到其中
$ mkdir hello-world
$ cp deps.edn hello-world
$ cd hello-world
$ mkdir src
預設情況下,clj
工具會在 src
目錄中尋找原始檔。建立 src/hello.clj
(ns hello
(:require [java-time.api :as t]))
(defn time-str
"Returns a string representation of a datetime in the local time zone."
[instant]
(t/format
(t/with-zone (t/formatter "hh:mm a") (t/zone-id))
instant))
(defn run [opts]
(println "Hello world, the time is" (time-str (t/instant))))
您可能會決定將此應用程式的一部分移至函式庫。clj
工具使用本機座標來支援僅存在於您的本機磁碟中的專案。讓我們將此應用程式的 java-time 部分抽取到平行目錄 time-lib 中的函式庫中。最後的結構將類似於以下內容
├── time-lib │ ├── deps.edn │ └── src │ └── hello_time.clj └── hello-world ├── deps.edn └── src └── hello.clj
在 time-lib 中,使用您已有的 deps.edn
檔案的副本,並建立一個檔案 src/hello_time.clj
(ns hello-time
(:require [java-time.api :as t]))
(defn now
"Returns the current datetime"
[]
(t/instant))
(defn time-str
"Returns a string representation of a datetime in the local time zone."
[instant]
(t/format
(t/with-zone (t/formatter "hh:mm a") (t/zone-id))
instant))
更新 hello-world/src/hello.clj
中的應用程式,以改用您的函式庫
(ns hello
(:require [hello-time :as ht]))
(defn run [opts]
(println "Hello world, the time is" (ht/time-str (ht/now))))
修改 hello-world/deps.edn
以使用本機座標,該座標指向 time-lib 函式庫的根目錄(請務必更新您電腦的路徑)
{:deps
{time-lib/time-lib {:local/root "../time-lib"}}}
然後,您可以透過執行應用程式,從 hello-world 目錄測試所有內容
$ clj -X hello/run
Hello world, the time is 12:22 PM
與他人分享該函式庫會很棒。您可以透過將專案推送到公開或私人 git 儲存庫,並讓其他人使用 git 相依性座標來達成此目的。
首先,為 time-lib 建立一個 git 函式庫
cd ../time-lib
git init
git add deps.edn src
git commit -m 'init'
然後前往公開的 git 儲存庫主機(例如 GitHub),並遵循指示建立和發布此 git 儲存庫。
我們還想要標記此版本,以便它具有有意義的版本
git tag -a 'v0.0.1' -m 'initial release'
git push --tags
最後,修改您的應用程式以改用 git 相依性。您需要收集以下資訊
儲存庫函式庫 - Clojure CLI 使用一個慣例,其中如果使用類似於 io.github.yourname/time-lib
的函式庫名稱,則不需要指定 GitHub URL https://github.com/yourname/time-lib.git
。
標籤 - v0.0.1
是我們在上面建立的
sha - 標籤中的簡短 sha,如果你有本機儲存庫,請使用 git rev-parse --short v0.0.1^{commit}
尋找它,或者如果你有遠端儲存庫,請使用 git ls-remote https://github.com/yourname/time-lib.git v0.0.1
尋找它。你也可以使用 GitHub 儲存庫查看標籤及其備份提交來尋找它。
更新 hello-world/deps.edn
以改用 git 座標
{:deps
{io.github.yourname/time-lib {:git/tag "v0.0.1" :git/sha "4c4a34d"}}}
現在你可以再次執行應用程式,使用(共用)git 儲存庫函式庫。你第一次執行它時,當 clj
下載並快取儲存庫和提交工作樹時,你會在主控台上看到額外的訊息
$ clj -X hello/run
Cloning: https://github.com/yourname/time-lib
Checking out: https://github.com/yourname/time-lib at 4c4a34d
Hello world, the time is 02:10 PM
現在你的朋友也可以使用 time-lib
了!
隨著你的程式變得更複雜,你可能需要建立標準類別路徑的變體。Clojure 工具支援使用別名修改類別路徑,別名是 deps 檔案的一部分,只有在提供相應別名時才會使用。你可以執行的一些事情包括
通常,專案類別路徑預設只包含專案來源,而不包含其測試來源。你可以在類別路徑建構的 make-classpath 步驟中,將額外路徑新增為主要類別路徑的修改。為此,新增一個包含額外相對來源路徑 "test"
的別名 :test
{:deps
{org.clojure/core.async {:mvn/version "1.3.610"}}
:aliases
{:test {:extra-paths ["test"]}}}
套用類別路徑修改,並透過呼叫 clj -A:test -Spath
來檢查修改後的類別路徑
$ clj -A:test -Spath
test:
src:
/Users/me/.m2/repository/org/clojure/clojure/1.11.2/clojure-1.11.2.jar:
... same as before (split here for readability)
請注意,測試目錄現在包含在類別路徑中。
你可以擴充前一節中的 :test
別名,以包含 cognitect-labs test-runner,用於執行所有 clojure.test 測試
擴充 :test
別名
{:deps
{org.clojure/core.async {:mvn/version "1.3.610"}}
:aliases
{:test {:extra-paths ["test"]
:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
:main-opts ["-m" "cognitect.test-runner"]
:exec-fn cognitect.test-runner.api/test}}}
然後使用預設設定執行測試執行器(執行 test/ 目錄下 -test 命名空間中的所有測試)
clj -X:test
deps.edn
檔案中的別名也可以用來新增會影響類別路徑的選用依賴項
{:aliases
{:bench {:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}}
這裡的 :bench
別名用於新增一個額外的依賴項,也就是 criterium 基準測試函式庫。
您可以透過新增 :bench
別名來修改相依性解析,將此相依性新增至您的 classpath:clj -A:bench
。
在不將函式庫新增至現有的 deps.edn
檔案或建立新檔案的情況下,對其進行實驗可能會有幫助。
$ clojure -Sdeps '{:deps {org.clojure/core.async {:mvn/version "1.5.648"}}}'
Clojure 1.11.2
user=> (require '[clojure.core.async :as a])
nil
請注意,由於跳脫規則,最好將設定資料用單引號包起來。
有些相依性在可以在 classpath 上使用之前,需要執行準備步驟。這些函式庫應在其 deps.edn
中說明此需求
{:paths ["src" "target/classes"]
:deps/prep-lib {:alias :build
:fn compile
:ensure "target/classes"}}
包含頂層金鑰 :deps/prep-lib
會告訴 tools.deps classpath 建構,需要額外準備此函式庫,而且可以透過在 :build
別名中呼叫 compile
函式來執行。準備步驟完成後,它應該會建立路徑 "target/classes"
,而且可以檢查是否已完成。
您依賴此函式庫就像依賴任何其他基於來源的函式庫(可能是 git 或本機)一樣
{:deps {my/lib {:local/root "../needs-prep"}}}
如果您接著嘗試將該函式庫包含在您的 classpath 中,您會看到一個錯誤
$ clj
Error building classpath. The following libs must be prepared before use: [my/lib]
然後您可以使用此命令告訴 CLI 進行準備(這是針對特定函式庫版本的一次性動作)
$ clj -X:deps prep
Prepping io.github.puredanger/cool-lib in /Users/me/demo/needs-prep
$ clj
Clojure 1.11.2
user=>
您可以同時使用多個別名。例如,此 deps.edn
檔案定義了兩個別名 - :old-async
用於強制使用較舊的 core.async 版本,而 :bench
用於新增額外的相依性
{:deps
{org.clojure/core.async {:mvn/version "0.3.465"}}
:aliases
{:old-async {:override-deps {org.clojure/core.async {:mvn/version "0.3.426"}}}
:bench {:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}}
如下所示啟用兩個別名:clj -A:bench:old-async
。
偶爾您可能需要直接參照 Maven 儲存庫中不存在的磁碟 jar,例如資料庫驅動程式 jar。
使用指向 jar 檔案而非目錄的本機座標,指定本機 jar 相依性
{:deps
{db/driver {:local/root "/path/to/db/driver.jar"}}}
使用 gen-class 或 gen-interface 時,必須先期編譯 Clojure 來源才能產生 java 類別。
這可以透過呼叫 compile
來完成。編譯類別檔案的預設目的地是 classes/
,需要建立並新增至 classpath
$ mkdir classes
編輯 deps.edn
以將 "classes"
新增至路徑
{:paths ["src" "classes"]}
在 src/my_class.clj
中使用 gen-class 宣告類別
(ns my-class)
(gen-class
:name my_class.MyClass
:methods [[hello [] String]])
(defn -hello [this]
"Hello, World!")
然後,您可以在另一個原始檔 src/hello.clj
中使用 :import
參照類別。請注意,命名空間也已新增至 :require
中,因此編譯可以自動找出所有依賴的命名空間並編譯它們。
(ns hello
(:require [my-class])
(:import (my_class MyClass)))
(defn -main [& args]
(let [inst (MyClass.)]
(println (.hello inst))))
您可以在 REPL 中編譯或執行指令碼來進行編譯
$ clj -M -e "(compile 'hello)"
然後執行 hello 命名空間
$ clj -M -m hello
Hello, World!
請參閱 編譯與類別產生 以取得完整參考。
Clojure 提供內建支援來執行 socket 伺服器,特別是使用它們來主機遠端 REPL。
若要設定 socket 伺服器 REPL,請將下列基本設定新增至您的 deps.edn
{:aliases
{:repl-server
{:exec-fn clojure.core.server/start-server
:exec-args {:name "repl-server"
:port 5555
:accept clojure.core.server/repl
:server-daemon false}}}}
然後透過呼叫別名來啟動伺服器
clojure -X:repl-server
如果您願意,您也可以在命令列中覆寫預設參數(或新增其他選項)
clojure -X:repl-server :port 51234
您可以使用 netcat 從另一個終端機連線
nc localhost 51234
user=> (+ 1 1)
2
使用 Ctrl-D 退出 REPL,使用 Ctrl-C 退出伺服器。
內建 :deps
別名中有多個有用的工具,可探索專案使用的所有傳遞式依賴項(及其授權)。
若要 列出類別路徑中包含的所有依賴項,請使用 clj -X:deps list
。例如,在本指南開頭的 hello-world
應用程式中,您會看到類似以下內容
% clj -X:deps list
clojure.java-time/clojure.java-time 1.1.0 (MIT)
org.clojure/clojure 1.11.2 (EPL-1.0)
org.clojure/core.specs.alpha 0.2.62 (EPL-1.0)
org.clojure/spec.alpha 0.3.218 (EPL-1.0)
time-lib/time-lib ../cli-getting-started/time-lib
應用程式使用的所有傳遞式依賴項會依照字母順序列出,並附上版本和授權。請參閱 API 文件以取得其他列印選項。
如果您想要了解依賴項的 樹狀 結構以及如何進行版本選取,請使用 clj -X:deps tree
% clj -X:deps tree
org.clojure/clojure 1.11.2
. org.clojure/spec.alpha 0.3.218
. org.clojure/core.specs.alpha 0.2.62
time-lib/time-lib /Users/alex.miller/tmp/cli-getting-started/time-lib
. clojure.java-time/clojure.java-time 1.1.0
這裡沒有進行任何版本選取,但請參閱 文件 以進一步了解在需要時如何在樹狀結構中說明選取。
這兩個輔助函數都接受一個選用的 :aliases
引數,如果您希望使用一個或多個別名來檢查依賴項清單或樹狀結構,例如 clj -X:deps list :aliases '[:alias1 :alias2]'
。
原始作者:Alex Miller