J9.03 (に入る予定) の新機能

J 言語のバージョン 9.03 がもうすぐリリースされるので、それに先立って前のバージョンからの大きな変更点を紹介します。

全ての変更点を見るには、wiki のリリースノートを参照してください :

https://code.jsoftware.com/wiki/System/ReleaseNotes/J903

# Nameref Caching

通常 J では名前の参照は、その名前を含む式を実行するたびに探索されます。しかし、public な verb/adverb/conjunction は一度定義されれば滅多に書き換えられることはありません。再定義・削除されないと分かっている場合に名前探索の回数を減らすために、nameref caching が実装されました。

スクリプトの始めの方で `9!:5(1)` を実行することで、それ以降の部分で nameref caching が有効になります。(`9!:5(0)` で無効化することもできます。)

nameref caching は、以下のいずれにも該当**しない**名前に対して適用されます。

- 値が noun である
- numbered locale [^1] に格納されている
- (explicit definition/direct definition において) 引数やローカル変数の名前である
- `'name'~` や `name__loc` を用いて名前が動的に生成される

キャッシュは、名前の参照が最初に解決されるときに作られ、それ以降は同じ参照について再び探索が行われることはありません。(例え参照先の名前が変更されても、影響を受けません。)

numbered locale を使っている場合でも、adverb `4!:8` を使ってキャッシュが有効な名前参照を作ることができます。

# Self-Effacing References

self-effacing reference [^2] は、名前を削除して、そこに格納されていた値を返す機能です。

`name_:` のように、`_:` で終わる名前が self-effacing reference です。先述の通り、`name` を削除して、元々 `name` に代入されていた値を返すだけです。

あまり役に立たないようにも見えますが、大きな配列を扱っている場合などに、値のコピーを回避する [^3] 手段として使えます。

また、単に名前を削除する機能としても有用です。(`erase` を使うより短く済みます。)

# Pre-Parsed Parenthesized Primitives (PPPP)

最適化関連の機能として他に、pre-parsed parenthesized primitives (略称 PPPP [^4]) があります。explicit definition/direct definition の定義の時点で modifier などを先にパースしておく最適化です。

以下の条件を満たすものが、PPPP として扱われます。

- 括弧 `( )` で囲まれている
- 名前を含まない
- 代入・`!:`・explicit definition を含まない
- verb を実行しない

具体的には、`(>:@i.)` などです。

また、二重の括弧 `(( ))` を使えば、verb を事前に実行することも可能です (例 : `((i.10))`)。ただし、この場合も、他の条件は全て満たしている必要があります。

# Modifier Trains

これは新機能ではなく、初期のバージョンにあった機能です。一度削除されていて、このバージョンから復活することになったようです。

簡単に言えば、**hook や fork の modifier (adverb/conjunction) 版**です。組み合わせが増えるので少し複雑になりますが、基本的に hook と fork に基づいているので、覚えるのは難しくないと思います。

表の V・N・A・C は それぞれ verb・noun・adverb・conjunction を表します。VN は、V または N です。modifier の引数は u と v で示しています。

|train|値|備考|
|---|---|---|
|V₀ N₁|(noun)|verb の実行 (monad)|
|N₀ V₁ N₂|(noun)|verb の実行 (dyad)|
||||
|V₀ V₁|(verb)|hook の作成|
|A₀ V₁|( u A₀) V₁||
|C₀ C₁|( u C₀ v ) ( u C₁ v )||
||||
|VN₀ V₁ V₂|(verb)|fork の作成|
|A₀ V₁ V₂|( u A₀ ) V₁ V₂||
|C₀ V₁ V₂|( u C₀ v ) V₁ V₂||
|V₀ V₁ C₂|V₀ V₁ ( u C₂ v )||
|C₀ V₁ C₂|( u C₀ v ) V₁ ( u C₂ v )||
|A₀ A₁ V₂|(u A₀) (v A₁) V₂||
||||
|VN₀ A₁|(any)|adverb の実行|
|A₀ A₁|( u A₀ ) A₁||
|C₀ A₁|( u C₀ v ) A₁||
|A₀ A₁ A₂|( ( u A₀ ) A₁ ) A₂||
|C₀ A₁ A₂|( ( u C₀ v ) A₁ ) A₂||
||||
|VN₀ C₁ VN₂|(any)|conjunction の実行|
|A₀ C₁ VN₂|( u A₀ ) C₁ VN₂||
|C₀ C₁ VN₂|( u C₀ v ) C₁ VN₂||
|VN₀ C₁ A₂|VN₀ C₁ ( u A₂ )||
|VN₀ C₁ C₂|VN₀ C₁ ( u C₂ v)||
|A₀ C₁ A₂|( u A₀ ) C₁ ( v A₂ )||
|A₀ C₁ C₂|( u A₀ ) C₁ ( u C₂ v )||
|C₀ C₁ A₂|( u C₀ v ) C₁ ( v A₂ )||
|C₀ C₁ C₂|( u C₀ v ) C₁ ( u C₂ v )||
||||
|C₀ VN₁|u C₀ VN₁|conjunction の部分適用 (右)|
|VN₀ C₁|VN₀ C₁ u|conjunction の部分適用 (左)|
|A₀ C₁|( u A₀ ) C₁ u||

さらに、ユーティリティとして**新しい modifier** が追加されます [^5]。

- `]:` (adverb) : `u]:` は `u` を返します。
- `[.` (conjunction) : `u[.v` は `u` を返します。
- `].` (conjunction) : `u].v` は `v` を返します。

# 所感

個人的には、modifier trains が一番楽しみです [^6]。コードゴルフやパズルが好きな人にとっては、とても魅力的な機能だと思います。

J は古い言語ではありますが、絶えず更新され続けています。特に J9.01 以降、fold family [^7] や direct definition など、新機能の追加も活発に行われています。同時に使用頻度の低い機能の削除などもされていて、より一層洗練された言語になってきている印象があります。

~~書きかけの J 言語入門記事が ずっと下書きのところにあるのに目を背けながら、~~ J9.03 のリリースを待ちたいと思います。

[^1]: `cocreate` で作った locale のことです。
[^2]: self-effacing を訳すと「控えめな」「謙虚な」といった意味になりますが、ここでは `efface` (消去する) の意味合いが強いような気もします。
[^3]: J の配列はイミュータブルですが、参照されていない値は自由に書き換えることができるので、in-place に verb を実行するような最適化が可能な場合があります。
[^4]: ヒョウ柄の人が果物にペンを刺す歌ではありませんよ。
[^5]: verb の `[` と `]` に対応する機能です。
[^6]: この記事を書いたのも実はそれが理由だったりします。
[^7]: 他の言語の fold や reduce に相当する機能群です。