6.1 3-train, the invisible combinator

combinatorは5. Modifier Glyphsにて紹介したが、実はあれだけでは記述できない構造がある。

    ( 𝕨𝔽𝕩 ) 𝕊 ( 𝕨𝔾𝕩 )

このなんということはない構造もcombinatorを一つ用意すればいいように思えるが、 問題はAPLやBQNの大原則:関数の引数は高々2個という制約の下では この式に出現する3種類の関数𝔽,𝔾,𝕊をcombinatorに渡しようがないことである。 もちろんuncurry化してしまえばいいのだが、このよく使うであろうパターンに対して直感的でない記述を強いてしまう。なんとかならないだろうか。

APLの開発者たちが考案した巧妙な方法はcombinatorを書くのをやめよう(だからthe invisible combinator)、3つの関数が項を構成していたら自動的にこの制御構造だとみなそうというものである12

      ( 𝔽𝕊𝔾 ) 𝕩 → ( 𝔽𝕩 ) 𝕊 ( 𝔾𝕩 )     # monadic version
    𝕨 ( 𝔽𝕊𝔾 ) 𝕩 → ( 𝕨𝔽𝕩 ) 𝕊 ( 𝕨𝔾𝕩 )   # dyadic version

これで基本的な関数間の配線に困ることはなくなる(困るようならローカル変数やブロック構造を使おう)。 この「3つの関数(だけ)から構成された項」を3-trainと呼ぶ (APLではforkとも呼ぶ)。 逆に3つ(以上の)関数を括弧で囲む際には3-trainとして解釈されてもよいか気を付ける必要があるので注意が必要である。

また2-trainは「2つの関数(だけ)から構成された項」であり、単なる2関数の関数合成である。

      ( 𝔽𝔾 ) 𝕩 → 𝔽 ( 𝔾𝕩 )
    𝕨 ( 𝔽𝔾 ) 𝕩 → 𝔽 ( 𝕨𝔾𝕩 )

BQNでは2-train (𝔽𝔾)は明示的に2-modifier Atop''を使って'𝔽∘𝔾とも書ける。Atopを使った方が1文字少なくて済む。

一般化してtrainは「複数の関数(だけ)からなる項」であり、 4つ以上の関数が項を構成している場合は右から3-trainとして合成関数に置き換えていくことで最終的には一つの合成関数になる。

設問1

    (0<≠)

上のコードは以下のどれに該当するか。

  1. 構文エラー
  2. 2-train
  3. 3-train

解答

3は恒等関数である。このことがわかっていないと以下のような無駄なコードを書いてしまう。

    (0⊸<≠)

一方、この'auto type/role coercion'は2-trainでは働かない。

   (10×)2
Error: Second-level parts of a train must be functions
at (10×)2
    ^^
   (10×⊢)2        # 3-trainなら問題ない
20
   (10⊸×)2        # あるいはtrainにしない
20
   10⊸×2          # ちなみに括弧は不要
20
   10×2           # ちなみにこれはdyadic applicationに帰着する
20

なんだかよくわからない。

設問2

以下はtrainか。

    # 1.
    (+⊸×⟜-)
    # 2.
    (×1-⌊)
1

https://aplwiki.com/wiki/Trainによれば Dyalog APLに導入されたのは2014年のことなのでAPLの古い説明には出てこない。 ということで日本語ではほぼ読むことができない。

2

3-trainを「括弧で囲まれた〜」とする説明は間違いである。 その定義では以下のF, Gが3-trainになる理由を説明できない。

    3+×-2    # 3 + (× (- 2)) という関数適用の連鎖であり3-trainではない
2
    F ← +×-
    3 F 2      # (3+2)×(3-2) → 5
5
    g ← 1⊑⟨+,+×-,-⟩
    3 G 2      # (3+2)×(3-2) → 5
5

Try it online⌨️

括弧で囲まれていることは条件ではなく、複数の関数が項を構成していることが必要であり、式の中ではそのような状況は括弧で囲まないと滅多に出現しないだけである。

3-trainとbefore+after bindsの違い

3-trainがよく似たbefore+after bindsとは違う構造を持っていることを確かめておこう。

    2(|⋈×)5    # 3-train
⟨1 10⟩
    2-⊸⋈⟜×5    # before+after binds
⟨2 1⟩

前者の3-trainは、

    𝕨 ( 𝔽 𝕊 𝔾 ) 𝕩 → ( 𝕨 𝔽 𝕩 ) 𝕊 ( 𝕨 𝔾 𝕩 )

と変換されるのに対して、後者のbefore+after bindsは、

    𝕨𝔽 ⊸ 𝕊 ⟜  𝔾 𝕩 → ( 𝔽 𝕨 ) 𝕊 ( 𝔾 𝕩 )

と変換されるのでどちらも必要になる。

なお、この話は "Combinatory Logic and Combinators in Array Languages"3の7.12でも触れられている。

設問

以下の構造を2-modifierや3-trainなどの組み合わせで実現せよ。

    𝕨 F ( 𝕨 G 𝕩 )
   (⊣FG)