Clojure

因为工作需要(写 jepsen test),不得不学习这门语言。这是一门 lisp 系纯函数式的语言。我还没忘记第一次读 SICP 时 lisp 给我带来的伤害,看起来 Clojure 也是括号满地,但是事到如今也只能硬着头皮上了。

学习

  1. learnxinyminutes:学语言首选,并且它下面也推荐了几个其他网址,方便快速入门,我觉得挺好的。
  2. official quickref:肯定少不了来这里查。可以学到许多 builtin functions。
  3. Solving Problems the Clojure Way - Rafal Dittwald:函数式思想入门,茅塞顿开
  4. Clojure 风格指南
  5. Writing Macros - Clojure for the Brave and True

开发环境

vscode 安装 Calva Spritz 即可。

Ctrl + Shift + P,create a mini clojure project。然后点击下方的 REPL(一个可以实时查看执行结果的东西),打开(Jack-in)即可。一般选择 Babashka,因为其他两个都打不开。

用法只需要掌握:在当前代码行上,按 Alt + Enter 执行单句。如果有调用前面的函数,先按 Ctrl + Shift + c, Enter 引入依赖,再执行单句。(什么邪恶快捷键)

更多可以看 The Top 10 Calva Commands。

语言基础

零散知识点。

和其他语言对比:

项目管理

包管理

一般使用 deps.edn 作为包管理。project.clj 已经过时了。

clojure
{:deps
 {cheshire/cheshire {:mvn/version "5.11.0"}
  org.clojure/data.json {:mvn/version "2.5.0"}}}

使用 clj xxx.clj 运行程序时会自动下载依赖。依赖通过 maven 管理,下载位置在 ~/.m2。

序列化

如果只是需要 RPC,不涉及其他语言,可以用 nippy,这是一个快速的 binary 序列化库。

如果需要 json 等标准格式,可以用官方的 clojure.data.json 或者 cheshire。

不过要注意,无论是 clojure.data.json 还是 cheshire,对 key type 的处理都是一坨大便。

clojure
(ns test
  (:require [clojure.data.json :as json]))
(def my-data {:type :invoke, :f :txn, :value [[:w 2 1]], :time 3291485317, :process 0, :index 0})
(println my-data)
(def json-string (json/write-str my-data))
(println json-string)
(def deser-data (json/read-str json-string :key-fn keyword))
(println deser-data)

输出:

{:type :invoke, :f :txn, :value [[:w 2 1]], :time 3291485317, :process 0, :index 0}
{"type":"invoke","f":"txn","value":[["w",2,1]],"time":3291485317,"process":0,"index":0}
{:type invoke, :f txn, :value [[w 2 1]], :time 3291485317, :process 0, :index 0}

冒号被丢失了!

因此要解决这个问题,需要自行进行一些处理。

输出:

{:type :invoke, :f :txn, :value [[:w 2 1]], :time 3291485317, :process 0, :index 0}
{":type":":invoke",":f":":txn",":value":[[":w",2,1]],":time":3291485317,":process":0,":index":0}
{:type :invoke, :f :txn, :value [[:w 2 1]], :time 3291485317, :process 0, :index 0}

打包

将 clojure 打包为 jar 文件,方便调用。 假设我有一个 src/mytest.clj:

clojure
(ns mytest
  (:gen-class))
(defn -main
  [& args]
  (println (str "Hello, World!" args)))
老式:lein uberjar

官网有 tools.build Guide,但是讲得实在是抽象。因此我先尝试老式打包。

然后运行 lein uberjar 打包,java -cp target/myproject-0.1.0-standalone.jar clojure.main -m mytest 验证。也可以直接 java -jar target/myproject-0.1.0-standalone.jar。

运行 clj -T:build clean && clj -T:build uber 即可。(clj -T:build jar 是来凑数的不用管)

劝退