REPL交互环境 #

一、REPL简介 #

REPL(Read-Eval-Print-Loop)是Clojure交互式开发的核心。它提供了一种即时反馈的开发体验,让你可以边写边测试代码。

1.1 REPL工作流程 #

text
┌─────────────────────────────────────────┐
│                                         │
│   Read  →  Eval  →  Print  →  Loop     │
│    ↓        ↓        ↓        ↓        │
│   读取    求值    打印    循环          │
│                                         │
└─────────────────────────────────────────┘

1.2 启动REPL #

Clojure CLI

bash
clj

Leiningen

bash
lein repl

nREPL(网络REPL)

bash
lein repl :headless

二、基本操作 #

2.1 表达式求值 #

clojure
user=> (+ 1 2 3)
6

user=> (defn square [x] (* x x))
#'user/square

user=> (square 5)
25

2.2 多行输入 #

clojure
user=> (defn greet
  #_=>   [name]
  #_=>   (str "Hello, " name "!"))
#'user/greet

REPL会自动检测括号匹配,等待完整输入。

2.3 特殊命令 #

命令 功能
(quit)(exit) 退出REPL
(doc name) 查看文档
(source name) 查看源码
(find-doc "text") 搜索文档
(apropos "text") 搜索符号
(pst) 打印堆栈跟踪

三、命名空间操作 #

3.1 当前命名空间 #

clojure
user=> *ns*
#object[clojure.lang.Namespace 0x... "user"]

3.2 创建命名空间 #

clojure
user=> (ns my-app.core)
nil

my-app.core=> *ns*
#object[clojure.lang.Namespace 0x... "my-app.core"]

3.3 切换命名空间 #

clojure
user=> (in-ns 'my-app.utils)
#object[clojure.lang.Namespace 0x... "my-app.utils"]

my-app.utils=> (ns user)
nil

3.4 查看命名空间内容 #

clojure
user=> (ns-publics 'clojure.string)
{:split #'clojure.string/split, :join #'clojure.string/join, ...}

user=> (ns-interns 'user)
{greet #'user/greet, square #'user/square}

user=> (ns-map 'user)
{...}

四、文档与源码 #

4.1 查看文档 #

clojure
user=> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
  Returns a lazy sequence consisting of the result of applying f to
  the set of first items of each coll, followed by applying f to the
  set of second items in each coll, until any one of the colls is
  exhausted. ...

4.2 查看源码 #

clojure
user=> (source when)
(defmacro when
  [test & body]
  (list 'if test (cons 'do body)))

4.3 搜索文档 #

clojure
user=> (find-doc "string")
user=> (find-doc #"regex")

user=> (apropos "map")
(map map? mapcat mapv ...)

4.4 查看元数据 #

clojure
user=> (meta #'map)
{:arglists ([f coll] [f c1 c2] ...),
 :doc "Returns a lazy sequence...",
 :line 2713,
 :column 1,
 :file "clojure/core.clj",
 :name map,
 :ns #object[clojure.lang.Namespace ...]}

五、调试技巧 #

5.1 打印调试 #

clojure
user=> (defn process [x]
         (println "Input:" x)
         (let [result (* x 2)]
           (println "Result:" result)
           result))

user=> (process 5)
Input: 5
Result: 10
10

5.2 使用pr和prn #

clojure
user=> (pr {:a 1 :b 2})
{:a 1, :b 2}

user=> (prn {:a 1 :b 2})
{:a 1, :b 2}
nil

5.3 堆栈跟踪 #

clojure
user=> (/ 1 0)
ArityException...

user=> (pst)
ArityException Dividing by zero
    clojure.lang.Numbers.divide (Numbers.java:163)
    user/eval123 (NO_SOURCE_FILE:1)
    ...
nil

5.4 使用tap> #

Clojure 1.10+ 提供的调试工具:

clojure
user=> (add-tap println)
#object[...]
user=> (tap> {:debug "data"})
{:debug "data"}
true
user=> (remove-tap println)

六、热重载 #

6.1 加载文件 #

clojure
user=> (load-file "src/my_app/core.clj")

6.2 重新加载命名空间 #

clojure
user=> (use 'my-app.core :reload)

user=> (require '[my-app.core :as core] :reload)

6.3 使用tools.namespace #

clojure
user=> (require '[clojure.tools.namespace.repl :refer [refresh]])

user=> (refresh)
:reloading (my-app.core my-app.utils)
:ok

七、REPL历史 #

7.1 历史导航 #

快捷键 功能
上一条命令
下一条命令
Ctrl+R 搜索历史

7.2 保存历史 #

Leiningen自动保存REPL历史到 .lein/repl-history

八、编辑器集成 #

8.1 Calva (VS Code) #

连接REPL

  1. 打开Clojure文件
  2. Ctrl+Alt+C Enter 连接
  3. Ctrl+Enter 求值当前表达式

常用命令

快捷键 功能
Ctrl+Enter 求值当前表达式
Alt+Enter 求值顶层表达式
Ctrl+Alt+C Space 求值选中代码
Ctrl+Alt+C Ctrl+L 加载当前文件

8.2 CIDER (Emacs) #

启动REPL

text
M-x cider-jack-in

常用命令

快捷键 功能
C-x C-e 求值表达式
C-c C-k 加载当前文件
C-c M-n 切换命名空间
C-c C-d d 查看文档

8.3 Cursive (IntelliJ) #

启动REPL

  1. 右键项目 → Run REPL
  2. 或使用 lein repl

常用操作

  • Ctrl+Shift+L 求值表达式
  • Ctrl+Shift+P 加载文件

九、高级技巧 #

9.1 自定义REPL提示符 #

clojure
user=> (set! *print-length* 10)
10

user=> (range 100)
(0 1 2 3 4 5 6 7 8 9 ...)

9.2 捕获异常 #

clojure
user=> (try
         (/ 1 0)
         (catch Exception e
           (str "Error: " (.getMessage e))))
"Error: Divide by zero"

9.3 时间测量 #

clojure
user=> (time (reduce + (range 1000000)))
"Elapsed time: 23.456 msecs"
499999500000

9.4 查看Java类 #

clojure
user=> (class "hello")
java.lang.String

user=> (class 42)
java.lang.Long

user=> (class [1 2 3])
clojure.lang.PersistentVector

十、REPL工具库 #

10.1 clojure.repl #

clojure
user=> (require '[clojure.repl :refer [doc source pst]])

user=> (doc map)

user=> (source when)

user=> (pst)

10.2 clojure.test #

clojure
user=> (require '[clojure.test :refer [deftest is run-tests]])

user=> (deftest my-test
         (is (= 1 1))
         (is (= 2 (+ 1 1))))

user=> (run-tests)

10.3 clojure.pprint #

clojure
user=> (require '[clojure.pprint :refer [pprint]])

user=> (pprint {:a 1 :b {:c 2 :d 3}})
{:a 1,
 :b {:c 2,
     :d 3}}

十一、最佳实践 #

11.1 开发流程 #

  1. 在编辑器中编写代码
  2. 发送到REPL求值测试
  3. 交互式调试
  4. 保存文件
  5. 定期刷新命名空间

11.2 REPL友好代码 #

clojure
(defn process-data
  [data]
  (let [cleaned (filter valid? data)
        transformed (map transform cleaned)]
    transformed))

将复杂逻辑拆分为小函数,便于在REPL中测试。

11.3 避免状态污染 #

clojure
user=> (ns my-test
         (:require [my-app.core :as core]))

my-test=> (core/process ...)

使用独立命名空间测试,避免污染主命名空间。

十二、总结 #

REPL开发的核心优势:

  1. 即时反馈:快速验证想法
  2. 交互调试:逐步排查问题
  3. 探索式编程:边写边学
  4. 热重载:无需重启应用

REPL使用技巧:

  • 熟练使用 docsourcefind-doc
  • 掌握编辑器集成快捷键
  • 利用 tap>pst 调试
  • 定期刷新命名空间

REPL是Clojure开发的核心工具,熟练使用REPL将大大提升开发效率!

最后更新:2026-03-27