定義ワード defer
(訳注: (期限を定めない)延期)を使用すると、 その振る舞いを定義せずに名前でワードを定義できます。
その振る舞いの定義は延期(defer)されます。 これが役立つ 2 つの状況を以下に示します:
以下の例では、 foo
は常に greet
の「Good breakfast
」を出力するバージョンを呼び出し、
bar
は常にgreet
の「Hello
」を出力するバージョンを呼び出します。
ソースコードを並べ替えて再コンパイルすることなく、 foo
で新しいバージョンを使用できるようにする方法はありません。
: greet ." Good morning" ; : foo ... greet ... ; : greet ." Hello" ; : bar ... greet ... ;
この問題は、 greet
を defer
された ワードとして定義することで解決できます。 defer
された
ワードの振る舞いは、 IS
を使用して以前に定義されたワードの xt と関連付けることによって、 いつでも定義および再定義できます。
上記の例は以下のようになります:
Defer greet ( -- ) : foo ... greet ... ; : bar ... greet ... ; : greet1 ( -- ) ." Good morning" ; : greet2 ( -- ) ." Hello" ; ' greet2 IS greet \ make greet behave like greet2
プログラミング・スタイル・メモ: すべての defer された ワードに対してスタック・コメントを記述し、 かつ、 そのスタック効果に一致する XT のみを defer されたワードに入れる必要があります。 そうしないと、 defer されたワードを使用するのは非常に困難です。
defer された ワードを使用すると、 User-defined Defining Words
の統計収集の例(statistics-gathering example)を改善できます。 アプリケーションのソース・コードを編集してすべての
:
を my:
に変更するのではなく、 以下のようにします:
: real: : ; \ retain access to the original defer : \ redefine as a deferred word ' my: IS : \ use special version of : \ \ load application here \ ' real: IS : \ go back to the original
注意すべき点の 1 つは、 IS
には特別なコンパイル機能(compilation semantics)があり、 (TO
のように)コンパイル時に名前をパースするということです:
: set-greet ( xt -- ) IS greet ; ' greet1 set-greet
IS
が適合しない状況では、 代わりに defer!
を使用してください。
defer された ワードは、 xt から実行機能(execution semantics)のみを継承できます(xt が表現できるのはそれだけであるためです – これについての詳しい説明は see Tokens for Words 参照)。 デフォルトでは、この実行機能(execution semantics)から派生したデフォルトのインタープリター機能(interpretation semantics)とコンパイル機能(compilation semantics)を持ちます。 ただし、 defer された ワードのインタープリター機能とコンパイル機能は、 通常の方法で変更できます。
: bar .... ; immediate Defer fred immediate Defer jim ' bar IS jim \ jim has default semantics ' bar IS fred \ fred is immediate
Defer
( "name" – ) core-ext “Defer”
defer された ワード name を定義します。 その実行機能(execution semantics)は defer!
または is
で設定できます(最初に name を実行する前に設定する必要があります)。
defer!
( xt xt-deferred – ) core-ext “defer-store”
xt (xt-deferred) で表される defer された ワードに実行のための xt を設定します。
IS
( xt "name" – ) core-ext “IS”
defer
された ワード name に実行のための xt を設定します。
defer@
( xt-deferred – xt ) core-ext “new-defer-fetch”
xt は、 defer された ワード xt-deferred に現在関連付けられているワードを表します。
action-of
( interpretation "name" – xt; compilation "name" – ; run-time – xt ) core-ext “action-of”
Xt は、 現在 name に割り当てられている XT です。
Forth-94 では、 これら Forth-2012 のワードの定義は、compat/defer.fs で提供されます。 さらに、 Gforth は以下を提供します:
defers
( compilation "name" – ; run-time ... – ... ) gforth-0.2 “defers”
defer された ワード name の現在の内容を現在の定義にコンパイルします。 つまり、 これにより、 name が defer されなかったかのように静的結び付け(static binding)が生成されます。
wrap-xt
( xt1 xt2 xt: xt3 – ... ) gforth-1.0 “wrap-xt”
defer された ワード xt2 の現在の xt-current をどこかに退避して xt1 に設定した上で、 xt3 を実行し、 xt3 実行後、 退避した xt-current を defer されたワード xt2 に戻します(訳注: xt3 の内部のどこかで defer されたワード xt2 を使っているとして、 xt2 の実行機能を一時的に xt1 に設定してから xt3 を実行し、 実行後に xt2 の実行機能を元に戻しておく。 元に戻すのは try ... restore ... endtry で囲まれた部分なので xt3 実行中にエラーがあっても確実に復旧される。 詳しくはソースコード見て下さい。 なお、 xt: は xt3 を deferフレーバーに設定する)
preserve
( "name" – ) gforth-1.0 “preserve”
指定の defer された ワード name で、 現在設定されている実行機能 xt を、 その場で is
や
defer!
したかのようなコードに変換します(訳注: 上記例より、 ’ greet2 is greet :
preserve-greet2 preserve greet ; → greet1 is greet ok → greet Good morning
ok → preserve-greet2 ok → greet Hello ok ; see preserve-greet2 → :
preserve-greet2 ‘greet2 ‘greet ! ; ok)