use-packageのマクロ展開を調査

本記事では [use-package](https://github.com/jwiegley/use-package) の 2.4.4 を対象に、マクロ展開後のコードを例示します。
対象とする読者は他の人の設定を参考に use-package を使っているが、内部処理の詳細を理解できていない方です。

本記事と合わせて @conao3 さんの https://qiita.com/conao3/items/dc88bdadb0523ef95878 を読んでいただくとより理解が深まります。
上記の記事にはleaf.elだけでなくuse-packageの解説が含まれます。
本記事で行うようなマクロ展開の例も含まれており、実質的にこの記事の上位互換の記事です。

なお筆者はSpacemacsを使用しているため、環境依存による差異があるかもしれません。

## use-package について
use-packageはパッケージの読み込みや設定を統一的なフォーマットで行えるようにするためのマクロです。
`:ensure` キーワードを使ってパッケージの読み込みをする機能はあるものの、本質的な用途はインストール済みのパッケージの設定ということになります。

当初、筆者は「マクロである」と言われても、あまりピンときませんでしたが、実際に展開されたコードを見ると本質的な機能はシンプルであることが分かりました。
デフォルトの設定のままマクロ展開を行ったコードにはエラー処理が含まれるため、シンプルであることに気づきづらいことも事実です。

本記事では事前設定としてuse-packageの設定をいくつか紹介し、その後`pp`, `macroexpand`の関数を使ったマクロの展開の例を紹介します。

## 事前設定
マクロ展開後のコードを簡略化するため下記の設定を行います。

### use-package-expand-minimally
```elisp
(setq use-package-expand-minimally t)
```

エラー処理などを省いた、簡略化したコードで展開をするオプションです。
docsには
> It can also help with reading macro-expanded
> definitions, to understand the main intent of what's happening.
> (翻訳) マクロ展開された定義を読むことで、主な処理の概要を理解することに役立ちます

とある通り、全体像の理解に役立つためこのオプションを有効にします。

### use-package-inject-hooks
```elisp
(setq use-package-inject-hooks nil)
```

default値はnilです。
spacemacs ではこのオプションが有効化されているため、nilに戻します
https://github.com/syl20bnr/spacemacs/blob/b28d65b7aa7a936cdab7fc0453df86b21ac1496f/core/core-use-package-ext.el#L75

### use-package-use-theme
```elisp
(setq use-package-use-theme nil)
```

`:custom` キーワードの展開後のコードを簡略化します。
このオプションに nil 以外の値を設定すると `:custom` で設定した値が custom file に保存されることを防ぐことができるオプションのようです。

## マクロ展開
README.md の「Getting started」セクションの設定例をマクロ展開してみます。

### 最もシンプルな例
キーワードを何も与えなければ、展開されるコードは require 関数1行のみです。
```展開前.el
(use-package foo)
```

```展開.el
(setq use-package-expand-minimally t)
(setq use-package-inject-hooks nil)

(pp
 (macroexpand
  '(use-package foo)))

;; (require 'foo nil nil)
```

### `:init`
`:init` キーワードに記載した処理は require 関数の前に実行されます。
```展開前.el
(use-package foo
  :init
  (setq foo-variable t))
```

```展開.el
(pp
  (macroexpand
    '(use-package foo
      :init
      (setq foo-variable t))))
;; (progn
;;   (setq foo-variable t)
;;   (require 'foo nil nil))
```

### `:init` と `:config` の併用
`:init` の処理は require の前に `:config` の処理はrequire関数の後に実行されます。

```展開前.el
(use-package foo
  :init
  (setq foo-variable t)
  :config
  (foo-mode 1))
```

```展開.el
(pp
 (macroexpand
  '(use-package foo
     :init
     (setq foo-variable t)
     :config
     (foo-mode 1))))
;; (progn
;;   (setq foo-variable t)
;;   (require 'foo nil nil)
;;   (foo-mode 1)
;;   t)
```

use-packageのREADME.mdではより複雑な例が記載されていますが、同様の方法でマクロ展開後のコードを調べることができます。

### `:init`, `:config`, `:custom`の併用
最後に `:init,` `:config`, `:custom` の3つのキーワードのマクロ展開例を記載します。

展開後のコードを見ると `:custom`, `:init` はrequire前に、`:config`はrequire後に出力されることが分かります。

```展開前.el
(use-package foo
     :init
     (setq foo-variable t)
     :config
     (foo-mode 1)
     :custom
     (bar-variables t)
     )
```

```展開.el
(setq use-package-use-theme nil)
(pp
 (macroexpand
  '(use-package foo
     :init
     (setq foo-variable t)
     :config
     (foo-mode 1)
     :custom
     (bar-variables t)
     )))
;; (progn
;;   (customize-set-variable 'bar-variables t "Customized with use-package foo")
;;   (setq foo-variable t)
;;   (require 'foo nil nil)
;;   (foo-mode 1)
;;   t)
```

## おわりに
筆者がuse-packageの設定中に陥った「`:custom` キーワードの処理はrequire**後**に呼び出される」という誤解が、記事執筆のきっかけとなりました。
実際には`:custom`キーワードの処理はrequire**前**に呼び出されます
(より正確には `use-package-keywords` の設定に準ずるが正しいはずです)

`pp` や `macroexpand` を使いながら展開後のコードを読み解く方法は、他のライブラリでも参考にできるため、自分向けの備忘録として公開しておこうと思います。

## 参考文献
  - [正式リリース]leaf.elで雑然としたEmacs設定ファイル「init.el」をクリーンにする
    - https://qiita.com/conao3/items/dc88bdadb0523ef95878
    - 「use-packageのキーワード解釈の非統一性」のセクションでは本記事と同じ内容の実行例を記述している