(rontolisp) docs

パッケージ

rontolispには、一連の組み込みパッケージとdefpackage によるユーザー定義パッケージを持つ小さな名前空間(パッケージ)システムがあります:

  • cl — 標準パッケージ。すべての組み込み関数、マクロ、特殊形式、および *package* 変数がここに属します。
  • cl-user — デフォルトの作業パッケージ。cl を 使用 するため、標準シンボルを修飾なしで利用できます。プログラム開始時のカレントパッケージです。ユーザ定義はここに置かれます。
  • rontolisp — 実装固有のシンボルのためのパッケージ。rl は組み込みのニックネームです。cl を 使用しません。version 関数を所有します。
  • linalg — numpy スタイルのベクトル・行列演算(linalg:zeros、linalg:matmul、linalg:solve など)。Lisp ソースで一度だけ実装され、すべてのバックエンドで利用できます。la は組み込みのニックネームです。cl を 使用しません。ベクトルと行列ガイドを参照してください。
  • torch — linalg カーネル上の、逆方向自動微分、nn スタイルのモジュール層、オプティマイザ、学習ループの補助を備えた PyTorch スタイルのテンソル (torch:tensor、torch:matmul、torch:backward、torch:linear、torch:cross-entropy-loss、torch:adam、torch:step など)。Lisp ソースで一度だけ実装され、すべてのバックエンドで利用できます。cl を 使用しません。ニューラルネットワークガイドを参照してください。
  • geom — linalg カーネル上のソリッドモデリング: 剛体変換、シーングラフ、三角形メッシュをキャッシュする境界表現の立体 (geom:box、geom:cylinder、geom:attach、geom:mesh、geom:volume など)。linalg と同様に Lisp ソースで一度だけ実装され、初回使用時に読み込まれます。linalg 以外に依存しないので、すべてのバックエンドで利用できます。cl を 使用しません。ソリッドモデリングガイドを参照してください。
  • java — リフレクションによる Java 連携。JVM インタプリタ (java -jar rontolisp.jar) でのみ使え、コンパイラやネイティブバイナリでは使えません。cl を 使用しません。new、call、static、field、proxy を所有します。Java 連携ガイドを参照してください。
  • objc — Foreign Function API による Objective-C ランタイムと AppKit へのバインディング。macOS 専用で、インタプリタ (java -jar rontolisp.jar と rontolisp ネイティブバイナリ)、コンパイル済み .class / .jar、Apple シリコン向けの --native 実行ファイルで使え、.wasm では使えません。cl を 使用しません。LispWorks 8.1 の Objective-C インターフェースの名前 (invoke、invoke-bool、invoke-into、retain、release、define-objc-class、define-objc-method など)、ブロック (make-objc-block、with-objc-block など)、コンディション objc-exception と ns-error、そして独自の on-main、data、bytes、objectp を所有します。隣の cocoa は Foundation の構造体と通知のオブザーバを、fli は LispWorks の外部言語インターフェースのうちマニュアルの例が使う部分 (define-foreign-function、外部オブジェクト、ポインタ) を持ちます。macOS GUI ガイドを参照してください。
  • appkit — objc の上の Cocoa ウィジェット層 (appkit:window、appkit:label、appkit:button、...)。linalg と同様に Lisp ソースで一度だけ実装され、初回使用時に読み込まれます。cl を 使用しません。同じガイドを参照してください。
  • metal — objc の上に載る、appkit ウィンドウ上の Metal 描画サーフェス (metal:attach、metal:library、metal:pipeline、metal:buffer、metal:frame、metal:run など)。どの Metal プログラムも同じように書くレイヤ、デバイス、コマンドキュー、レンダーパスをまとめています。Lisp ソースで一度だけ実装され初回使用時に読み込まれます。objc と同じく macOS 専用で、単独で成立します (geom や scene は不要)。cl を 使用しません。同じガイドを参照してください。
  • scene — metal の上に載る geom ソリッドの 3D ビューア (scene:viewer、scene:add、scene:fit、scene:camera、scene:animate など)。軌道回転・パン・ドリーのカメラ、地面グリッド、座標軸、アニメーションフックを備え、フレーム中に三角形単位の処理を行いません。Lisp ソースで一度だけ実装され初回使用時に読み込まれます。metal と同じく macOS 専用です。cl を 使用しません。ソリッドモデリングガイドを参照してください。
  • asdf — ASDF の限定的な API 互換サブセット(システム定義): defsystem と load-system。cl を 使用しません。システムガイドを参照してください。
  • ql — Quicklisp の限定的な API 互換サブセット: quickload は本物の Quicklisp ディストリビューションからシステムをダウンロードし、asdf サブセットを経由してロードします。update-dist は dist の index を更新します。quicklisp は組み込みのニックネームです。cl を 使用しません。システムガイドを参照してください。
  • ql-dist — Quicklisp のディストリビューション管理パッケージ。プログラムが書くメンバーは install-dist の 1 つで、Quicklisp 形式の別のディストリビューション (Ultralisp や任意の distinfo URL) を ql:quickload の検索対象に加えます。cl を 使用しません。システムガイドを参照してください。
  • uiop — ASDF の移植性レイヤ。15 個のサブパッケージ (uiop/os、uiop/pathname など) として登録され、uiop がそれらを再エクスポートするので、メンバのどちらの綴りも同じシンボルを指します。cl を use しません。uiop パッケージ を参照してください。
  • usocket — rontolisp:tcp-* ソケット組み込みの上に載った usocket 互換シム(usocket:socket-connect、usocket:socket-listen など)。Lisp ソースで一度だけ実装され、組み込み ASDF システム "usocket" としても登録されています。cl を 使用しません。TCPソケットガイドを参照してください。

シンボルはパッケージ修飾子で参照できます: package:symbol(例: cl:car、rontolisp:version)はパッケージの external(export 済み)シンボルに届き、package::symbol は internal を含む任意のシンボルに届きます — Common Lisp と同じシングル/ダブルコロンの区別です(external シンボルと internal シンボルを参照)。*package* はカレントパッケージを保持し(find-package が返すパッケージキーワードなので (eq *package* (find-package ...)) が成り立ちます)、(in-package name) はそれを切り替えます(名前はキーワード、シンボル、または文字列です: :rontolisp、rontolisp、"rontolisp")。Common Lisp と同様に *package* はフォームの実行時に読まれる動的変数です: 関数は呼び出し時点のカレントパッケージを読み、(let ((*package* ...)) ...) はその範囲で束縛し、with-standard-io-syntax は cl-user に束縛し、setq で代入できます。標準の Common Lisp 名 common-lisp と common-lisp-user は cl と cl-user の組み込み ニックネーム なので、ポータブルな (:use #:common-lisp) clause や common-lisp:car の参照も解決されます。短縮名 rl と la は rontolisp と linalg の、quicklisp は ql の組み込みニックネームです。ユーザーパッケージは defpackage の :nicknames clause で独自のニックネームを登録できます。

rontolisp:version は rontolisp --version と同じ情報を、プロパティリスト (:version "0.1.0-SNAPSHOT" :build-timestamp "..." :git-commit "..." :git-branch "...") として返します。タイムスタンプとリビジョンは実行中のビルドに由来するため、ここでは固定の結果を示していません。

rontolisp パッケージは cl を使用しないため、その中では標準シンボルを cl: で修飾する必要がありますが、(所有している)version は修飾なしで利用できます:

(in-package rontolisp)
(cl:print (version))           ; the rontolisp package owns version
(cl:print (cl:car '(1 2)))     ; standard symbols need the cl: prefix here
;; (car '(1 2)) would be an error: Undefined symbol: car (use cl:car)

デフォルトパッケージ cl-user は空で cl を使用するため、通常のプログラムでは修飾子は不要です。

external シンボルと internal シンボル

Common Lisp と同様、各パッケージは external(export 済み)シンボルと internal シンボルを区別し、2 つの修飾子の綴りで届く範囲が異なります:

  • package:symbol(シングルコロン)は external シンボルのみを参照します。
  • package::symbol(ダブルコロン)は internal を含むパッケージの 任意の シンボルを参照します。

組み込みパッケージはドキュメント化された API 全体を export しています: 標準の cl シンボルはすべて external で、本マニュアルに載っている rontolisp・java の関数もすべて external です(そのためダブルコロンが 必須 になることはありませんが、rontolisp::version も受け付けられ、 rontolisp:version と同じ意味になります)。internal シンボルは % プレフィックス規約に従います — 例えば rontolisp:json-parse の背後にある 固定引数ヘルパー rontolisp::%json-parse — これらは実装詳細であり、予告なく 変わることがあります。cl-user は Common Lisp の COMMON-LISP-USER パッケージと同じく何も export しないため、まれに cl-user のシンボルに 修飾子が必要な場合は cl-user::name と書きます。

external でないシンボルへのシングルコロンでの参照は read/コンパイル時に エラーになります:

CL-USER> (rontolisp:%json-parse "1")
Error: The symbol %json-parse is not external in the rontolisp package (use rontolisp::%json-parse)

パッケージの export セットは定義時に決まり — 組み込みパッケージはドキュメント化 された API を、ユーザー定義パッケージは (:export ...) clause の内容を export します — その後は export/unexport で調整できます。(in-package rontolisp) が有効な間に定義されたシンボルは rontolisp パッケージに internal シンボルとして intern されるため、他の パッケージからはダブルコロンで参照する必要があります。

export が変えるのはシンボルに到達できる修飾子であって、どのシンボルであるかは 変わりません。そのため export は公開する定義の前でも後でも構いません。Common Lisp との相違が1点あります: 最初に名前が現れた後で export されたシンボルは、アクセスできないパッケージから 表示時にダブルコロンのままになります — ここでは修飾子は表示時に計算されるので はなくシンボルに保持されているためです — が、どちらの綴りも同じシンボルを指します。

修飾子を印字するかどうかは Common Lisp のアクセス可能性の規則に従います: prin1/print(および ~S)は、現在の *package* からアクセスできるシンボル — そのパッケージ自身のシンボル、:use を通じて継承した external シンボル、import したシンボル — には修飾子を付けず、それ以外は pkg:name / pkg::name と 印字します。princ/~A は修飾子を印字しません。したがって下の helper は (in-package :mypkg) の下では HELPER、cl-user からは MYPKG::HELPER と 印字されます。未対応が1点あります: cl-user のシンボルや標準シンボルを、 アクセスできないパッケージから印字しても COMMON-LISP-USER::name / COMMON-LISP:name ではなく修飾子なしで印字されます。

ユーザー定義パッケージ(defpackage)

新しいパッケージは defpackage で定義します:

in-package と同様に、defpackage は read/コンパイル時に消費されるリテラルな トップレベルディレクティブ であり、パッケージは使用より前に、ソース順に 定義されます。サポートされる clause は (:use package...)、 (:export symbol...)、(:nicknames name...)、 (:import-from package symbol...)、(:shadowing-import-from package symbol...)、 (:shadow symbol...)、(:intern symbol...)、および受理されるが 無視される (:documentation "...")/(:size n) です。名前と clause の引数は キーワード、裸のシンボル、文字列、文字(#\H は "H" を指します)、または uninterned シンボル(#:name、 ポータブルな defpackage の慣用形)です。:shadow された名前はパッケージ内では 常にそのパッケージ自身のシンボルに解決され、cl(や使用パッケージ)の同名 シンボルには決して解決されません — これによりライブラリは独自の digit-char-p や defconstant を定義できます。:intern は export せずに パッケージが所有する名前を追加します — ただし使用先パッケージがその名前を export している場合は、Common Lisp と同様にそのシンボルを継承します。 それ以外の clause、まだ存在しない パッケージの使用はエラーです。:size と :documentation はそれぞれ一度だけ 指定できます。:shadow、:shadowing-import-from、:import-from、:intern に 与える名前は互いに素でなければならず、:intern と :export も同様です (違反は program-error)。import 元パッケージにない名前の import は package-error で、その continue restart は名前を import 元に intern します。 rontolisp はプログラムが読んだだけのシンボルを記録しないため、この検査は defpackage/make-package で作られ、まだどのソースも読み込まれていない パッケージに限られます。実行時に走る defpackage(関数本体の中、または eval 経由)はこれらを handler が捕捉できるコンディションとしてシグナルし、 トップレベルのものは read/コンパイル時に失敗します。 既に存在するパッケージを名前に指定した defpackage は、そのパッケージを 変更します(Common Lisp のルール) — clause は既存の内容にマージされ、これに より rontolisp が既に seed 済みのパッケージをライブラリ側が宣言できます。 他のパッケージの nickname を名前に指定した場合は依然としてエラーです。

  • :use は、使用するパッケージの external シンボルを修飾なしで見えるように します(Common Lisp と同様) — 使用先パッケージの internal シンボルには 依然としてダブルコロンが必要です。:use clause がなければ何も継承されない (SBCL と同様)ため、cl シンボルには cl: プレフィックスが必要になります。 通常のパッケージでは (:use :cl)(ポータブルには (:use #:common-lisp))と 書いてください。複数の使用先パッケージが 同じ名前を export している場合、:use 順で最初のパッケージが優先されます (Common Lisp はコンフリクトをシグナルします)。
  • :export はパッケージの external シンボルを宣言します。後から intern される シンボル((in-package name) の下で定義され :export clause に含まれない defun や自由変数)は、組み込みパッケージとまったく同様に internal です。 パッケージが定義せずに継承している名前 — cl 経由の標準名 ((:use :cl) (:export #:car))や使用先パッケージの export — は再エクスポート されます: mypkg:car は cl の car であり、mypkg を use するパッケージは cl を use していなくてもそのシンボルを継承します。
  • :nicknames は、正規名が解決されるすべての場所(修飾子、in-package、:use など)で解決される別名を登録します。既存のパッケージやニックネームと衝突する ニックネームはエラーです — 組み込みニックネーム(common-lisp、 common-lisp-user、rl、la、quicklisp)も組み込みパッケージ名と同様に 予約されています。
  • :import-from は、パッケージ全体を use せずに、1 つのパッケージの指定シンボル だけを修飾なしで見えるようにします。解決はテキストベースです: import された 名前はソースパッケージの正規表記に解決されるため、import して re-export した シンボルの mypkg:name は元の定義を参照します。

use-package は :use clause の実行時版で、 in-package と同じ読み込み/コンパイル時のルールに従います: リテラルな トップレベルの (use-package :mypkg) は、それ以降のフォームに対して現在の パッケージの use リストを広げます(すべてのバックエンドで動作します)。

unuse-package はその逆操作で、 export、unexport、 import も同じルールに従います。 make-package はこの読込/compile 時層の隣にある実行時層です: 空パッケージを作成し(名前は大文字化、:use 項目は既知のパッケージでなければなりません)、 rename-package が改名し、 delete-package が削除します。失敗は捕捉可能な package-error を signal します。読込/compile 時パッケージは実行時に不変です -- 改名も削除も signal します。defpackage の成果物がどちらの層に入るかは、誰が解決したかで決まります: コンパイル済みプログラムは綴りを焼き込むためその成果物は読込/compile 時層、 インタープリタは生きたレジストリに対して解決するため実行時層に入り、改名も 削除もできます。(トップレベルでない)他のフォームの中の defpackage は、その フォームが実行されたときに実行時層へパッケージを登録します -- インタープリタは これをサポートし、登録先のレジストリを持たないコンパイル済みバックエンドは 拒否します。

パッケージはメンバーテーブルを持ちます。intern、 export、import、 shadowing-import、shadow がそこに入れたものと、unintern が取り除いたものです。 find-symbol はインターン前の名前には nil を、 インターン後にはそのシンボル(と :internal / :external / :inherited のステータス)を返し、use-package はエクスポートされたメンバーを継承させ、 do-symbols / with-package-iterator はテーブルと use リストが持ち込むものを、それぞれコードが綴るとおりの綴りで 走査します。ソース中でパッケージの下に読まれただけのシンボルは記録されません -- その下で行われた定義は数えます(defun はインターンです)。インタープリタでは すべてのパッケージのテーブルが生きています。コンパイル済みバックエンドでは、 プログラムが make-package で作ったパッケージのテーブルは生きており、 読込/compile 時パッケージは凍結されています(変更系の操作はそこでは何も 変えずに t を返します)。

パッケージは読み込み/コンパイル時に(ソース順で)解決されるため、in-package はトップレベルのディレクティブです: ソース中のシンボルがどのパッケージに属するかは、その上にある in-package で決まり、実行時の *package* への setq では決まりません(コンパイル出力ではファイル全体が実行前に解決されます。インタプリタはトップレベルフォームに到達するたびに解決するため、そこでは実行時の代入が後続のフォームに影響します)。コンパイル出力では、実行時に読み込まれたファイルのパッケージディレクティブは処理されません。rontolisp パッケージの関数(version ...)は第一級の値として利用できません(mapcar/funcall に渡せません)。また cl を使用しないパッケージ内では、cl シンボル名をローカル変数としてシャドウしてはいけません。

rontolisp パッケージの拡張

rontolisp パッケージが所有するシンボルは 実装固有であり、Common Lispの一部ではありません。rontolisp: 修飾子で参照する(または (in-package rontolisp) の後に修飾なしで使用する)必要があります。version に加え、このパッケージは rontolisp:fetch (future を返す) と、rontolisp:await (解決)、 rontolisp:futurep (型述語) を通じて非同期の外向きHTTPを提供し、さらに rontolisp:json-parse / rontolisp:json-stringify によるJSON変換(JavaScriptの JSON.parse/JSON.stringify 相当)を提供します。これらはすべて 関数 リファレンスに独自のページを持ち、完全な rontolisp:fetch / rontolisp:await / rontolisp:futurep ドキュメントも含まれます。

このパッケージのメンバーのうち2つは関数でもマクロでもなく、read 時リテラルです: rontolisp:current-file と rontolisp:current-line で、リーダがそのシンボルの位置に置換します。in-package ディレクティブの解釈より前に解決されるため、これらは上記の規則の例外で、常に修飾付きで書く必要があります。 ソース位置リテラルを参照してください。