老工具万岁:一个原始人(grug-brained)的 Clojure 程序员的 REPL 习惯
Aditya Athalye writes and works here.
☙ read ❧ see ✉ mail λ self » now ≈ feed 目前正在进行的项目:"为极客写作"。 老工具万岁:一个原始人(grug-brained)的 Clojure 程序员的 REPL 习惯 [ ↓ toc ] 发布时间:2025-03-28 更新时间:2025-03-28 作者:Aditya Athalye
这是一个关于 Grug 如何进行 Clojure 代码开发的演示。由于某些原因,他对云 LLM 的魔法并不感冒。仍然喜欢只用脑力来编程。喜欢使用 Grug 语言的标准库和标准开发工具。 喜欢在本地磁盘上创建和使用所有字节。也许是勒德分子,也许已经过时。Grug 并不介意。他很满意自己的编码方式。此外,Grug 喜欢用脑力。希望你们能看到一些可以借鉴的技巧、诀窍和策略。 取其精华,去其糟粕。
✉ email me comments » share → [bsky] [in] [HN] [lobste.rs] [reddit] feed Email Tags: #meta#riff#howto#whyto#clojure#tools_for_thought#programming#ai#intelligence_augmentation Contents Video demos of two contrasting workflows Summary opinion for my demo "Slide deck"… whoami whereami Polyglot notebook FTW (sales pitch for org-mode :) Inspect live object Capture values to inspect them at will Try Out dependencies Footnotes
两种对比鲜明的工作流程的视频演示
在 SciCloj visual-tools 第 32 次会议上展示 (更多信息请访问 clojureverse)。
我的演示与我的朋友、同事、Clojure 爱好者和全能的人 Kapil Reddy 的演示形成了鲜明对比 (Kapil Reddy)。
- 首先,Kapil(从大约 15 分 50 秒开始):他如何使用 LLM/MCP 以及 ClojureScript 和 Clojure。
- 接下来,我(从大约 46 分 50 秒开始):老工具万岁:一个原始人(grug-brained)的 Clojurist 的 REPL 习惯。
Clojure visual-tools 32 - 工作流程演示 6:老派工具、REPL、Emacs、Org-mode、AI、MCP 等。 2025-03-26 的这次会议是系列会议中的第六次,人们在会上演示他们使用不同工具的 Clojure 工作流程。
关于我的演示的总结性观点
注意:有些偏向于我使用的 JVM Clojure。
- 培养操纵实时 Clojure 运行时的核心肌肉记忆,这种记忆不依赖于任何编辑器或第三方工具。
- 尽可能依赖 Clojure 标准库,尤其是命名空间、clojure.pprint、clojure.repl、clojure.reflect、clojure.datafy。
- 倾向于使用开箱即用的 deps and cli 和 build 工具,即使它们带来了更费力的学习曲线1 比方说,Leiningen。
- 在你的工作流程之上构建一个笔记本,比如 Clerk 或 Clay 或 Gorilla REPL,或者 Calva's Clojure Notebooks 功能。 或者我一直以来最喜欢的 org-mode 与 org-babel。
- 将这种动态工作流程移植并用于其他(非 Clojure)上下文中,例如,使用结构化提示技术来协调多个 LLM。 有关更多信息,请参阅我的同事 Kapil 正在做的事情(同一视频),并听取讨论。 这超出了我的能力范围。
“幻灯片”…
…它只是一个普通的 org-mode 文件,你将看到它被用来进行实时编码演示,我已经将它渲染成这篇博文。
whoami
- 机械同情的行走的悖论
- 迅速变得过时
- 对依赖关系严重过敏
- 只有一半的工作记忆
- 必须写作才能思考
- 无法使用 LLM,小脑瓜无法处理大型上下文2
- "为极客写作"(在吊床上)>> "为极客进行 Vibe 编码"
whereami
- evalapply.org/tags/clojure
- youtube.com/@evalapplydotorg
- github.com/adityaathalye
- linkedin.com/in/adityaathalye
Polyglot notebook FTW (sales pitch for org-mode :)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-1>)(ns user
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-2>) (:require [ring.adapter.jetty :as jetty])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-3>) (:gen-class))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-4>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-5>)(defn echo-handler [request]
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-6>) {:status 200
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-7>) :headers {"Content-Type" "text/html;charset=utf-8"}
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-8>) :body (pr-str request)})
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-9>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-10>)(defn run-jetty
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-11>) [port]
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-12>) (println "Starting Jetty server at port:" port)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-13>) (jetty/run-jetty echo-handler
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-14>) {:port port :join? false}))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-15>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-16>)(defn -main []
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-17>) (run-jetty 3000))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-18>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-19>)(comment ; "'Rich' comment form"
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-20>) ;; clojure -M -m org.evalapply.catchall-app
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-21>) ;; OR:
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-22>) (def server (run-jetty 3001))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-23>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-24>) (.stop server)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb1-25>))
And check…
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb2-1>)curl "localhost:3001"
And pipeline…
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb3-1>)curl -s "localhost:3001" | bb -e '(-> *input* :headers)'
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb4-1>)(require '[clj-http.client :as http])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb4-2>)(http/get "http://localhost:3001")
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb5-1>)(require 'clojure.pprint)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb5-2>)
Inspect live object
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb6-1>)(-> server
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb6-2>) clojure.reflect/reflect
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb6-3>) ;; alt: cider pprint from edit buffer
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb6-4>) clojure.pprint/pprint)
Capture values to inspect them at will
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-1>)(defonce responses (atom []))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-2>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-3>)(defn capture-response
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-4>) [response]
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-5>) (swap! responses conj response))
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-6>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-7>)(add-tap capture-response)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-8>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-9>)(tap> {:status 200 :body "hi"})
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-10>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb7-11>)@responses
Tabulate
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb8-1>)(clojure.pprint/print-table [:status :body] @responses)
Visual inspector
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb9-1>)(require '[clojure.inspector :as inspector])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb9-2>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb9-3>)(inspector/inspect-tree (clojure.reflect/reflect server))
Try Out dependencies
- Add without modifying deps.edn file
- refactor echo-handler to handle json=true query parameter
- copy down and replace body (dynamic REPL-driven dev.)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-1>)(require 'clojure.repl.deps)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-2>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-3>)(clojure.repl.deps/add-libs
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-4>) {'org.clojure/data.json {:mvn/version "2.5.1"}})
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-5>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-6>)(require '[clojure.data.json :as json])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-7>)(defn TODO-override-echo-handler [])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-8>)
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-9>)(require '[clj-http.client :as http])
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-10>)(let [base-uri "http://localhost:3001"]
[](https://www.evalapply.org/posts/demo-clojure-workflow-scicloj/<#cb10-11>) (http/get (str base-uri "/foobar?json=true")))
Footnotes
- Leiningen 提供了一个不错的、易于使用的“UI”。 而构建到 Clojure 发行版中的工具是“低级”机制。 学习它们需要一天左右的试错和阅读。 也许我们会在某个时候得到一个简单的包装器。 在此之前,值得付出努力来采用它,因为“你的构建是程序”的模型非常通用,并且驱动该模型的机制始终可以在 Clojure 可用的任何地方使用。↩︎
- Grug 过程看起来不像好莱坞黑客电影。 看起来像一部长达数小时的无声艺术电影……阅读-思考,尝试-思考,写作-思考,行走-思考,睡觉,吊床-阅读-思考,做快速 REPL 事情,测试,提交,部署,循环。↩︎
↑ toc ↑ title ↑ menu ✉ email me comments » share → [bsky] [in] [HN] [lobste.rs] [reddit] feed Email © 2025, Aditya Athalye. All content licensed CC BY-SA 4.0 , except where noted otherwise. Built with shite, GNU Emacs, org-mode, and 🖤. My newsletter is powered by Buttondown. Want to become a better programmer? Join the Recurse Center!