Clojureの環境構築

# Clojure環境構築

## 概要

spec, malliを試したかったので、Clojureの環境をMac上に構築した際の備忘録です。

## インストール

以下の記事が詳しいので、そちらを参照してください

- https://memoribuka-lab.com/?p=3569


### ツール

- Leiningen (必須)
  - Clojureのプロジェクト管理ツール

```sh
brew install leiningen
```

- Jocker (必須ではない)
  - Clojureを実行するための軽量なインタプリタ 

```sh
brew install candid82/brew/joker
```

### VSCode拡張機能

VSCodeの拡張機能は以下２つをインストールする

- [Calva: A Clojure & ClojureScript IDE in Visual Studio Code](https://calva.io/)
  - REPL環境などをいい感じに取りまとめてくれる拡張機能
- [cljfmt](https://marketplace.visualstudio.com/items?itemName=pedrorgirardi.vscode-cljfmt)
  - フォーマッタ
- ~~Clojure Linter (Joker)~~
  - 存在はするがアルファ版らしくイマイチ機能しないのでインストールしない

`settings.json`に以下を追加する  
(Command+Shift+P でパレットを開いて "settings" と入力すると見つかる)

```sh
{
  〜〜（省略）〜〜
  "[clojure]": {
    "editor.defaultFormatter": "pedrorgirardi.vscode-cljfmt",
    "editor.wordSeparators": "`~!@#$%^&*()=+[{]}\\|;:'\",.<>/?",
    "editor.formatOnSave": true
  }
}
```

- 参考
  - https://github.com/craig-johnston/selectWord/issues/2

### コマンド

Calvaのコマンドは以下リンクを参照

- https://calva.io/paredit/

`kyebinding.json`で追加設定する場合のCalvaコマンドのキー名はVSCodeの拡張機能ページや以下を参照

- https://github.com/BetterThanTomorrow/calva-paredit/blob/master/package.json

#### よく使うコマンド

- Ctrl+w
  - カーソル下の関数などを対応する括弧まで選択
- Ctrl+Enter
  - 選択箇所、もしくはカーソル下をREPLに送信して評価する
  - Ctrl+wで特定の処理を選択してCtrl+Enterで評価するという使い方になる
- Alt+Enter
  - カーソル下をREPLに送って評価する
  - 関数などの全体を選択状態にしていなくても全体を評価してくれる

##### Ctrl+wの変更

vimバインディングで実行しているので、Ctrl+wは直前の単語の消去であってほしい。  
デフォルトのキーバインドを変更する場合は以下のように設定を追加すれば良いです。

`keybindings.json`を開いて以下の設定を追加する  
(Command+Shift+P でパレットを開いて "keybind" と入力すると見つかる)

```json
    {
        "key": "ctrl+w",
        "command": "deleteWordLeft"
    },
    {
        "key": "alt+w",
        "command": "paredit.sexpRangeExpansion"
    }
```

## プロジェクトの作成

まず以下の方法でLeiningenのプロジェクトを作成します

```sh
lein new sample
```

メインファイルは`src/sample/core.clj`になるので、このファイルをベースとして修正していきます。

```clojure
(ns sample.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

(defn -main []
  (println "OK"))
```

### REPLによる実行

REPLにより実行する場合は、まずREPLサーバを起動する必要があります。  
以下のいずれかの方法で起動します。

- VSCodeの左下の`REPL`と書いてあるボタンをクリックする(10秒ぐらいかかる)

- コマンドパレットから `Start your project with A REPL and connect (a.k.a. Jack-in)`を選択する
  - Command + Shift + P でコマンドパレットを起動

REPLの部分の色が変わったら利用可能となります。

## 実行方法

以下のいずれかの方法で実行することになります。

- REPLから実行
- コマンドから実行
- スクリプトファイルで実行(Jokerなし)
- スクリプトファイルで実行(Jokerあり)

## REPLから実行

先ほど起動したREPLに対してコードを送信して実行する形式です。  
先程の`core.clj`ファイルに以下の内容を追記します。

```clojure
(println "OK")
```

`println`の部分にカーソルをあわせて、`Alt+Enter`を押下すると以下のように表示されます。

```
clj꞉sample.core꞉> 
; OK
nil
```

## コマンドから実行

プロジェクトのホームフォルダに移動して以下を実行します。

```sh
# ホームフォルダに移動
cd sample
# -main関数を実行する方法
lein run -m sample.core
# foo関数を実行する方法
lein run -m sample.core/foo 10
```

`-main`実行時に`-m`による名前空間の指定を省略したい場合は`project.clj`に`main`の設定を追加すると良いです。

```clj
(defproject sample "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :main sample.core
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :repl-options {:init-ns sample.core})
```

```sh
# -main関数を実行する方法
lein run
```

### スクリプトファイルで実行(Jokerなし)

この方法は起動までが遅いので非推奨です

Clojureの[公式サイト](https://clojure.org/releases/downloads)から最新版の以下の3ファイルをDLします。

- clojure-1.11.1.jar
- core.specs.alpha-0.2.62.jar
- spec.alpha-0.3.218.jar

私の環境では以下のように`/opt`下にディレクトリを作成して置きました。

```sh
sudo mkdir /opt/clojure
sudo chown -R ユーザ名 /opt/clojure
mv clojure-1.11.1.jar /opt/clojure/
mv core.specs.alpha-0.2.62.jar /opt/clojure
mv spec.alpha-0.3.218.jar /opt/clojure
```

以下のスクリプトファイルを作成します

```clojure
#^:shebang '[
  CLOJURE_HOME=/opt/clojure
  CP1=$CLOJURE_HOME/clojure.jar
  CP2=$CLOJURE_HOME/core.specs.alpha-0.2.62.jar
  CP3=$CLOJURE_HOME/spec.alpha-0.3.218.jar
  exec java -cp $CP1:$CP2:$CP3 clojure.main "$0" "$@"
]

(println "OK")
```

### スクリプトファイルで実行(Jokerあり)

以下のファイルを `ok.clj` として作成します。  
`joker ok.clj` によりファイルの中身を実行することができます。

```clojure
(println "OK")
```

## Tips
### REPLに接続すると lein command not found と言われる

以下に該当するTrouble Shootingがあるが、これが原因ではなかった

- https://calva.io/connect/#command-not-found-errors-when-jacking-in

VSCodeのCalva拡張機能に更新があったらしく、左下の歯車はから更新を実行したら接続するようになった...

## 参考
- Clojureをスクリプト実行する
  - https://tnoda-clojure.tumblr.com/post/24969285880/clojure-scripting
-  Leiningenでプログラムをコンパイル・実行する
   - https://scrapbox.io/lagenorhynque/%E3%80%90Clojure%E5%85%A5%E9%96%80%E3%80%91_Leiningen%E3%81%A7%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%82%92%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%83%BB%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B
- 環境構築(Clojure + VSCode)
  - https://memoribuka-lab.com/?p=3569
- Leiningen
  - https://qiita.com/FumiIwase/items/228a9c8dfdfbcde02d92
- Joker
    - https://github.com/candid82/joker#installation