(rontolisp) docs

関数名前空間

rontolispはCommon Lispに倣った Lisp-2 です。関数と変数は別々の名前空間に存在します。

  • 裸のシンボルは 変数 として評価されます。car 単独の評価はエラーです(インタプリタでは The variable car is unbound、コンパイラではコンパイルエラー)。
  • 呼び出し位置 (f args...) のシンボルは関数名前空間のみで解決されます。car という名前の変数が関数 car をシャドウすることはありません。(let ((car 5)) (car (list car 2)))5 を返します。
  • 関数は #'name((function name) のリーダ構文)、#'(lambda ...)(symbol-function 'name) を通じて になります。これは組み込み演算子(#'+, #'car, #'1+, #'cadr)、ユーザ定義の defun、ラムダで機能します。
  • funcall/mapcar/reduce は関数を指す シンボル(関数指定子)も受け付けます。(funcall 'car '(1 2))1 を返します。コンパイラはシンボルが引用されたリテラルである場合にこれをサポートします。
  • defun は関数名前空間に定義し、関数名を返します。(setq f (lambda ...))変数 を関数値に束縛します。これは (f ...) ではなく (funcall f ...) で呼び出します。
  • マクロまたは特殊演算子の #'(例: #'if, #'defun)はエラーです。

関数値は、3つの実行モードすべてで、引数として渡したり、関数から返したり、データ構造に格納したりできます。

高階関数:

クロージャ(参照によるキャプチャ):

引数としてのラムダ:

第一級の値としての組み込み演算子:

+, car, 1+ のような組み込み演算子は #' を通じて高階関数に渡せます:

コンパイラの制限。 JVM/WASMコンパイラでは、#'name はコンパイル時に判明している関数(ユーザ定義の defun と組み込み演算子)に対して解決されます。#'reduce#'apply 自体はここでは値として利用できません(インタプリタでは利用できます)。#'mapcar#'mapcan#'sort#'funcall は利用できます。symbol-function は引用されたシンボルリテラルの引数を要求します。--dynamic モードでは、未解決の #'name は他の未解決参照と同様にランタイムの eval 環境に委ねられます。コンパイルされたコードでは、apply/funcall は各組み込み演算子のために合成された固定アリティのラッパーに対して、実際の引数の数でディスパッチします。本来可変長の演算子 -- +-*/listminmax -- には可変長ラッパーがあるため、(funcall #'+ 1 2 3)(apply #'list ...) などは任意の引数の数を受け取ります。それ以外の複数引数の組み込みは固定アリティのラッパーのままです。#'cons#'append#'gcd や比較チェイン(#'<#'=、...)は 2 引数なので、異なる数に適用することはコンパイルパスではサポートされません( コンパイルされた eval の制限 に一致します)。それ以外のアリティにはユーザ定義関数か lambda を使ってください。インタプリタにはそのような制限はありません。

COMMON-LISP 関数の再定義

(defun random ...)(defun length ...) のような定義は Common Lisp では未定義動作であり(CLHS 11.1.2.1.2)、rontolisp のバックエンドも実際に異なります:

  • インタプリタは呼び出しを関数セル経由で解決するので、あなたの定義が実行されます。
  • JVM / WASM コンパイラは呼び出し位置で標準の演算子を認識してインラインに出力するので、あなたの定義は実行されません。演算子名と最初の該当呼び出し位置を示すコンパイル時警告を出力するため、この食い違いが黙って起きることはありません。

#'random はどのバックエンドでもあなたの定義を指します。警告が「定義はその形でなら到達できる」と述べているのはこのためです。どこでも同じ定義を使いたいなら、自分の名前を付けるか、自分のパッケージに shadow してください。組み込み名への defmethod は別の話で、どのバックエンドでも動きます: そのジェネリックのデフォルトメソッドになります。