2015年11月7日土曜日

CommonLispでpackageとか名前空間とか コロン(:) について知ったこと

概要

  • Common Lisp の package について
    • 解説したリンクを集めた
    • package について説明を試みた
    • package について疑問に思ったことをまとめた

参照リンク集

CommonLispでの Packageとは?

  • Lispの関数名も変数名も シンボル として扱われることを納得しておく必要がある
    • 関数名、変数名、スペシャル関数名、マクロ名 etc
  • シンボルが 所属 する シンボルの管理単位
    • 一部の例外を除く(!)すべてのシンボルは パッケージに属している
    • 例外1:gensym で生成されたシンボルは どのパッケージにも属さない
    • 例外2:キーワード - キーワード(keyword)という 特別なパッケージに属している
  • Common Lisp の第一級オブジェクトである(後述)
  • CommonLisp が標準で持っている関数やマクロや変数(シンボル)は
    cl, common-lispという名前空間に属している
  • REPLで実行して一時的に作られた and/or
    特にパッケージを明示せず定義されたシンボルは
    cl-user, common-lisp-user という名前空間に属している
    • REPL のデフォルトの package である

シンボルは第一級オブジェクトである、とは?

なんでも定義・変更できる(?) Common Lisp の例にもれず、package も各種操作を受け付けるということ
  • パッケージ(という値)の定義、削除
  • パッケージの名前の定義、削除、変更、ニックネームの定義
    • やっぱりパッケージを表すシンボル なんだろうか。調べきっていない
  • パッケージの中身=シンボル、の追加、削除
  • パッケージの外側に公開(export)するシンボルの決定
  • 他パッケージに属するシンボルを自パッケージに包含する
  • 自他パッケージに属するシンボルを「隠す」「上書きする」
    • shadowing? ここがまだよくわかっていない、あるいは困るまで調べない
    • 関数のオーバーライドと呼ばれる機構に近いらしい
  • 現在のパッケージ を切り替える
  • 各種調査用関数

パッケージのシンボルに対するアクセス方法 … コロン(:) の意味

  • package-name:symbol-nameexport されているシンボルにアクセス
    • packageが読み込み済みである必要がある
    • ppcre:scan ql:quickload asdf:load-system など
    • あるライブラリが提供している関数を使うときはこの方法を使う
  • package-name::symbol-name で パッケージ内の任意のシンボルにアクセス
    • exportされていなくてもアクセス可能
    • private, friend, protected 、、、なんてものは存在しない、完全ノーガード
    • shadowingしたらどうなるんだろう?
    • この手段を使うのは冷静に考えて邪道

コロン(:) を使う特別なシンボル…キーワード について

  • :keyword のように、コロン(:)から始まるシンボルをキーワードという
    • 何もない名前空間を参照しているわけではなかった
  • キーワードは 評価するとシンボル自身を返す
    * :test
    
    :test
  • キーワードは、keywordパッケージに属する
    • という扱いらしい。

Common Lisp のソースを読んでいて浮かぶ疑問

  • defpackage ? in-package ? なにこれ?
    • defpackage のところに、パッケージ名が書かれています。
    • :export という項目があれば、それが公開されている 関数、変数の一覧です
    • in-package はそのあとに定義したシンボルをパッケージに所属させるための
      定型文ですので、自分でライブラリを書くのでなければ気にしなくてよいです
  • ライブラリの関数なんかは ライブラリ名 + : + 関数名、で呼べるんだね?
    • だいたいそうです。cl-ppcre:scan とか asdf:load-system とか。
    • ライブラリ名とパッケージ名が違う可能性があるので注意してください
    • ニックネームが提供されていることがかなり多いです。 cl-ppcreppcre でもOK。
  • (あるはずの関数・変数について)そんなものないって怒られるんですけど
    • asdf, quicklisp でライブラリの「処理の中身とシンボル定義」を読み込む必要があります。
      • 読み込んだ後から使えるようになります
    • 自分でパッケージを扱いだすと、もう少し複雑な「シンボルがない」状況に直面すると思います
  • defsystem, defgeneric, defmethod, def** ってちがうの?
    • defsystem はわりと関係あるんですが、、、わかってません。ひとまず ASDF に関連してます
    • defgeneric, defmethod は CLOS ですね。まだよくわかってません。
    • だいたい「**を定義するマクロ」だと思って間違いありません。

他言語から流れてきた人間として思う疑問

  • namespace, using (C++, C#, Java) とは違うの?
    • namespace はかなり package に近い、クラスや関数の所属先を決めている
    • 違いは CommonLisp では シンボルは、定義された瞬間のpackageに属する こと
    • in-package で定義される 現在のパッケージ に属する
    • シンボルをパッケージに所属させることを intern というが
      任意のパッケージを指定できる
    • using の代わりに、defpackage の :use や use-package関数を使う
  • require(ruby), import(python) とは違うの?
    • common lisp にも require はある、、、いろいろまずくて誰も使ってないけど
    • ライブラリをひとまとまりで読み込む標準の機能はない
    • それは quicklisp や asdf が手助けしてくれる
      • はよ言語標準にならんかな
    • 個別ファイルであれば load を使うことで可能
    • using の時に書いた defpackage の、、、などが、そのpackageにあるシンボルを
      パッケージ名省略して使います、という宣言になる

まとめ。他の言語がむしろ、一つの文に複数の機能を備えていると言えるのでは。

  • 名前空間の定義
  • (名前空間に属する機能の)プログラムやデータの一括読み込み機構
  • 関数名、変数名、クラス名について、名前空間の明示的な省略
そしてLisperはおもむろにマクロを定義するのであろう。

ひとこと

  • Common Lisp における package に調べだしたところ、
    それがなんであるかについてはイメージを持てたのだが、
    いざ説明を書こうとして説明が難しすぎてさじ投げた
    • 伝わる書き方というのが結局見えなくなった
    • 特に、他言語の例については、ある程度理解が進むとむしろ説明が難しい
    • 何がわかっていなかったかわからなくなる
    • 他言語の例は自分が知っている言語をさらしているだけのような気がする
  • 投げたさじを拾ってわかっていることだけまとめておいておくことにした
    • 伝わるように書くことをあきらめた
  • Quick Reference に載っている関数を一通り試したのだが、
    一部の関数が思ったように動いてくれない。使い方を勘違いしているのだろう。
Written with StackEdit.

5 件のコメント:

  1. 英語なんですが http://www.flownet.com/ron/ の The Idiot's Guide to Common Lisp Packages (pdf) なんてのもあります。色々と動作例を示して、一番後ろに Final Thoughts があります。

    返信削除
  2. >nfunato さん
    コメントありがとうございます!

    さっそくPDF確認してみました、Final Thoughtを先に確認してみたのですが
    「パッケージは文字列をどうやってシンボルに変換するかをコントロールしている。他は何もしていない。」とは、、、!Zenめいており奥深さを感じます、、、。改めて読み直して試してみます!

    ぜひまたご教授いただければと思います。ありがとうございます!

    返信削除
    返信
    1. Common Lispにおけるpackageは、いわゆる名前空間の機能(例えば、複数のプログラマが一つの名前を別の用途に用いてしまっても、プログラム全体を書き換えずにそれらを一つのシステムの中で用いたりできるようにする)を提供するために導入されているわけですが、

      その実現手法としては、packageは名前(ここでは文字列)からシンボルへのマッピングを提供するようになって(http://www.lispworks.com/documentation/HyperSpec/Body/11_aa.htm)います。

      これが、Final Thoughts冒頭段落の第3文(Packages control ..., nothing else.)に書いてあることです。(第4文は、CLの他の知識に関連することなので、最初は無視してよいです)
      ここで、他の多くの言語と違うところは、package自体が第一級のLisp Objectになっていることです。つまり、実行時に(例えばREPLから対話的に)操作できます。もっとも、真剣な計算をしている最中に操作することは意図されておらず、プログラム環境の操作手段の一部として提供されているものと思います。

      あとは、Common Lisp のsymbolは、(少なくとも概念的には)、name, value, function, package, property-list の5つのスロットを持つ構造体である、ということを理解すればよいかと思います。

      削除
    2. おっと、最後のsymbolの構造についてはここにあります:
      http://www.lispworks.com/documentation/lw70/CLHS/Body/t_symbol.htm

      削除
  3. コメントありがとうございます!
    まずちゃんと定義があって文書にあり、そして自分は見つけられていないということに少々愕然とするところではあります。若干またかやってしまったか、、、とも思います、、、お教えいただいてありがとうございます!
    ちょっと私的に時間が取れず現在確認中の状態になっております。お教えいただいた内容と、少し確認した内容とで、概念としては理解が進んだようにように思います。記事のアップデートはまたしばらく先になりそうですが、、、。
    反応が遅くて恐縮ですが、ぜひまたご教授いただければ幸いです!コメントありがとうございます!

    返信削除