#:g1: defpackageでの#:symbolについて

Posted 2019-08-15 15:25:34 GMT

Common Lispのdefpackageというかパッケージ関係の関数全般ですが、internexportするシンボルの名前の表記にstring designatorが使えるので、

(defpackage "FOO" 
  (:use "CL")
  (:export "FOO" "BAR" "BAZ"))

とシンボル名を文字列で書かずにシンボルで書くことも可能です。

(defpackage foo
  (:use cl)
  (:export foo bar baz))

この場合、シンボル名が使われるので、

(defpackage #.(string 'foo) 
  (:use #.(string :cl))
  (:export . #.(mapcar #'string '(foo #:bar baz))))

のようなことになっていると考えれば良いでしょう。

このstring designatorでのシンボルの表記に各人割とこだわりがみられるので考察してみることにしました。

文字列そのまま

"FOO""BAR"等とそのまま書く流儀です。たまに通な人がこの方式で書いてることがあります。

メリットとされること

デメリットとされること

しかし、そんな特殊な状況のことを考えてコーディングする必要ってあるんですかね?

シンボル

foobar等とそのまま書く流儀です。
一番すっきりしてて良さそうですが、案外少ないです

メリットとされること

デメリットとされること

defpackageは、大抵cl-userでされることが多いですが、cl-userは作業用パッケージなので、多少汚染されても良いんじゃないかという気もしますね。

キーワードシンボル

:foo:bar等と書く流儀です。
よく見かける流儀です。

メリットとされること

デメリットとされること

キーワードパッケージが汚染されると開発環境のキーワード補完でゴミが補完されることが多くなるので、案外cl-userが汚染されるより嫌かもしれないですね。

自由(uninterned)シンボル

#:foo#:bar等と書く流儀です。
これも割と見かける流儀です。 自由(uninterned)シンボルは、不要になったらGCされるので、細かいことを気にする人に好まれています。

メリットとされること

デメリットとされること

第四の方法を考えてみた

見た目がウザいので自由(uninterned)シンボルで書きたくない、パッケージも汚染したくない、というのを両立させるとしたら、一時パッケージを作成し、その中でdefpackageすれば良いでしょう。

(defpackage "FOO-META" (:use))

(in-package "FOO-META")

(cl:defpackage foo (:use cl) (:export foo bar baz))

(cl:in-package "CL-USER") (delete-package "FOO-META")

SBCLならこんな風に書けたりもします。

foo-meta::
(cl:defpackage foo
  (:use cl)
  (:export foo bar baz))

まあ、でもめんどくさいですね。

まとめ

ファイルをロードする時に、暗黙のうちに*package*cl-user*readtable*を標準のリードテーブルであると仮定しているコードは、Quicklispの大半を占めますが、それに起因するバグも思いの外多いです(Quicklispのパッケージを1000パッケージ位ロードしてみると体験できます)

作業用パッケージ(とリードテーブル)を作成して、そこでdefpackageするのが吉なのかなあ等々考えていますが、作業用パッケージを作成するなら、余計なシンボルのインターンについても考えなくて良さそうですね。

ちなみにこの記事を書くにあたって、文字列、シンボル、キーワード、自由(uninterned)シンボルの大き目のリストを作成して読み取りの速度を計時してみましたが、大抵の処理系ではシンボルや文字列が速く、一番遅いのは自由(uninterned)シンボルのリストでした。
読み取りスピード的にも普通のシンボルで書くのが有利っぽいですが、極端なことをしない限りは有意な差にはならないでしょう。

以上、特にまとまりもない記事でした。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus