defpackageの:shadowと:shadowing-import-from

この記事は, [Lisp Advent Calendar 2019](https://qiita.com/advent-calendar/2019/lisp)の9日目の記事です.

この記事のライセンスは[CC-BY](https://creativecommons.org/licenses/by/4.0/deed.ja)とします.

この記事では, Common Lispの`defpackage`の`:shadow`オプションと`shadowing-import-from`オプションについて扱います.

1日目の記事[Common Lispでの開発実例 with package-inferred-system](https://qiita.com/MYAO/items/874aafcc531862c5f7c7)の捕捉記事的な側面も持ちますので,
宜しければそちらの記事もお読みください.

## TL;DR

- シンボルの名前の衝突を避けるために`:shadow`オプションを指定できる.
- 依存している複数のパッケージ間のシンボルの名前の衝突を避けて, 特定のパッケージのシンボルを使う場合には, `:shadowing-import-from`オプションを使って指定する.
- 別のパッケージから受け継いだシンボルについて, 関数定義や変数定義を行うと元のパッケージの関数や変数の再定義となるので注意が必要

## `defpackage`, `:export`, `:use`, `:import-from`

Common Lispでは, packageという物を定義することで, 関数や変数等を集めて, 名前空間を(他のpackage)分離することが出来ます.

Common Lispにはsymbolというものが存在して, これらは他の言語で言うところの変数(変数名)のようなものです. [^symbols]

[^symbols]: この説明は不正確だと思いますが, そもそも変数とは何かみたいなところからの説明をする知識と体力が著者になさそうなので, 詳しい方おられたらコメント等で補足してください.

packageは, symbolを他のpackageに提供したり, 他のpackageから提供されるsymbolを自身の中に受け継いだりすることが出来ます.

```common-lisp:sample.lisp
(defpackage :sample
  (:use :cl :foo)
  (:import-from :bar :hoge)
  (:export :piyo :fuga))
(in-package :sample)

(defun piyo () nil)
(defvar fuga 42)
(defun hogera (a b) (+ a b))
```

これは, packageの定義の例です. この例では, 最初の`defpackage`で`SAMPLE`というパッケージを定義しています.

`:export`オプションの引数に`:piyo :fuga`とあるので`PIYO`と`FUGA`という名前のシンボルを外部に提供します.

`:use`オプションの引数には, `:cl :foo`とあるので, パッケージ`CL`とパッケージ`FOO`が提供するすべてのシンボルを自身(パッケージ`SMAPLE`)の中に受け継ぎます.
例えばパッケージ`CL`は, `CAR`というシンボルを提供していますが, パッケージ`SAMPLE`の中では, `car`とパッケージ修飾子無しにアクセスできます.

この場合, `cl:car`や`cl::car`のようにしてもアクセスすることができます. これらはパッケージ修飾子付きのシンボルと呼ばれます.

`:import-from`オプションの引数には, `:bar :hoge`とあるので, パッケージ`BAR`から`HOGE`というシンボルをパッケージ`SMAPLE`の中に受け継ぎます.
この場合, パッケージ`SAMPLE`の中で, `hoge`の様にパッケージ`HOGE`はパッケージ修飾子無しにアクセスできます.

もし, パッケージ`BAR`がシンボル`BAZ`もexportしていたとすると, パッケージ`SAMPLE`の中で, `bar:baz`の様にコロンひとつのパッケージ修飾子付きのシンボルで, アクセスできます.
また, `bar::baz`の様にコロンふたつでもアクセスできます.

ちなみに, パッケージ`BAR`の中に, exportされていない`QUX`というシンボルがあるとするとこちらは`bar::qux`のようにコロンふたつでアクセスできますが, このアクセスの方法はおすすめ出来ません.

- あるパッケージがエクスポートしているシンボルをすべて受け継ぐ時には, `:use`を使います.
- あるパッケージから一部のシンボルを受け継ぐ場合, (あるいは一つもシンボルを受け継がない場合)には`:import-from`を使います.

この記事では, あるパッケージ内からパッケージ修飾子なしでアクセスできるようにすることを, シンボルをパッケージに受け継ぐと表現しています.

## `:shadow`

100のシンボルが定義されているパッケージから1つだけシンボルを受け継ぎたい場合には, `:import-from :xxx :yyy`の様に書けば良いということは前節で書きました.

逆のパターンを考えましょう. 100のシンボルが定義されているパッケージからひとつだけを除いた99のシンボルを受け継ぎたいという場合にはどうするかということです.

この様な場合には`:shadow`を用います.

この除くひとつのシンボルが`ZZZ`だとしましょう. (つまり`:zzz`と指定されるシンボル)

次のように`defpackage`を書くことで, シンボル`ZZZ`は受け継がれません.

```common-lisp:shadow-sample.lisp
(defpackage :shadow-sample
  (:use :cl :xxx)
  (:shadow :zzz))
(in-package :shadow-sample)
```

### 何のために受け継がないのか

ところで, 何故受け継がないという選択をしたいことがあるのでしょうか. 99個もシンボルを受け継ぐわけですから, 1個くらい受け継ごうが受け継がまいが関係なさそうにも思えます.

単純に, `xxx:zzz`と明示的にアクセスしたいと言うだけであれば, `:shadow`オプションを指定せずにその様に書いても良いはずです.
(気づかずに使ってしまうことを防げるくらいのことはあるかもしれませんが.)

実はそれ以上の意味があります. 次のコードを見てください.

```common-lisp:shadow-sample.lisp
(defpackage :shadow-sample
  (:use :cl :xxx))
(in-package :shadow-sample)
(defun zzz () 0)
```

先程の例と同じ様に, パッケージ`XXX`がシンボル`ZZZ`をエクスポートする状況だとします.

この時の`(defun zzz () 0)`は, パッケージ`XXX`の`ZZZ`を新しくここで定義した関数で再定義するという動作になります.
受け継がれたシンボルに対しては同じ動きをします.

`XXX`にも`SHADOW-SAMPLE`にも依存するパッケージがあり, そのパッケージから`XXX:ZZZ`を使うと,
`SHADOW-SAMPLE`で`XXX:ZZZ`が書き換えられたものが使われます.

```common-lisp:xxx.lisp
(defpackage :xxx
  (:use :cl)
  (:export :zzz))
(in-package :xxx)
(defun zzz () 42)
```

元のパッケージ`XXX`で上のコードのように`ZZZ`が定義されていたとしても, パッケージ`SHADOW-SAMPLE`で書き換えられているので, `(xxx:zzz)`は`42`ではなく`0`を返します.

これは困りますね. 無論わざとその様にするということもあるのかもしれませんが, あまりその様にして良いパターンは多くないでしょう.

`:shadow`オプションを使うとこの様なことがなくなります. `zzz`を`defun`や`defvar`などで関数変数定義に使わなくても,
パッケージ`SHADOW-SAMPLE`内にシンボル`ZZZ`が存在することになります.

`:shadow`オプションで複数のシンボルを指定することも出来ます.

`defun`や`defvar`で使うシンボル`ZZZ`を使うと`XXX:ZZZ`のことではなく`SHADOW-SAMPLE::ZZZ`のことを指すことになり, 関数の再定義は起こりません.

### 衝突を防ぐ

さて, 他のパターンを見てみます. パッケージ`VVV`もシンボル`ZZZ`をエクスポートしているとしましょう.

```common-lisp:xxx.lisp
(defpackage :shadowing-sample-2
  (:use :cl :xxx :vvv)
  (:shadow :zzz))
(in-package :shadowing-sample-2)
```

`XXX`と`VVV`の両方の`ZZZ`をパッケージ修飾子無しに使わないで良いのであればこのようにします.

もし, `:shadow`に`:zzz`を指定しなければ, `XXX`と`VVV`どちらの`ZZZ`か分からない, 別々の二つのものを同じ名前の一つのものとして扱おうとすることになり,
名前が衝突したとしてエラーになります.

## `:shadowing-import-from`

直前の例の場合で, `XXX`の`ZZZ`はパッケージ修飾子無しに使いたいとします. この様な場合には`:shadowing-import-from`を用います.

```common-lisp:xxx.lisp
(defpackage :shadowing-import-from-sample
  (:use :cl :xxx :vvv)
  (:shadowing-import-from :xxx :zzz))
(in-package :shadowing-import-from-sample)
```

この様に`:shadowing-import-from`オプションにパッケージ名続けてシンボル名(複数可能)を書くことで, 同名のシンボルを提供するパッケージから一つを選んで, そこからそのシンボルを受け継ぐことが出来ます.

この様にすることで, パッケージ`XXX`と`YYY`からの(両シンボル`ZZZ`による)の名前の衝突を防ぐとともに, パッケージ`XXX`からはシンボル`ZZZ`を受け継いで,
`SHADOWING-IMPORT-FROM-SAMPLE`パッケージ内で, シンボル`XXX:ZZZ`にパッケージ修飾子なしにアクセスすることが出来ます. [^redefined]

[^redefined]: `SHADOWING-IMPORT-FROM-SAMPLE`パッケージ内で, `(defun zzz () 1)`)のようにすると`XXX:ZZZ`が再定義される点は注意です.

## まとめ

- シンボルの名前の衝突を避けるために`:shadow`オプションを指定できる.
- 依存している複数のパッケージ間のシンボルの名前の衝突を避けて, 特定のパッケージのシンボルを使う場合には, `:shadowing-import-from`オプションを使って指定する.
- 別のパッケージから受け継いだシンボルについて, 関数定義や変数定義を行うと元のパッケージの関数や変数の再定義となるので注意が必要