コンピュータサイエンスにおいて、矢印またはボルトは、コンピュータプログラミングで計算を純粋かつ宣言的な方法で記述するために使用される型クラスです。コンピュータ科学者のジョン・ヒューズによってモナドの一般化として最初に提案された矢印は、計算における論理ステップ間の関係を参照透過的に表現する方法を提供します。 [ 1 ]モナドとは異なり、矢印はステップが1つの入力のみを持つことを制限しません。その結果、関数型リアクティブプログラミング、暗黙的プログラミング(ポイントフリースタイル)、パーサー、およびその他の用途で使用されています。[ 1 ] [ 2 ]
矢印は独立したクラスとして認識される以前から使用されていましたが、ジョン・ヒューズが矢印に焦点を当てた研究を初めて発表したのは2000年のことでした。それまでは、純粋なコードでプログラムロジックを組み合わせる必要があるほとんどの問題に対して、モナドで十分であることが証明されていました。しかし、グラフィカルユーザーインターフェース用のFudgetsライブラリや、特定の効率的なパーサーなど、いくつかの便利なライブラリは、モナド形式での書き換えが困難でした。[ 1 ]
矢印の形式概念は、モナドコードのこれらの例外を説明するために開発され、その過程で、モナドは矢印のサブセットであることが判明しました。[ 1 ]それ以来、矢印は活発な研究分野となっています。その基礎となる法則と操作は何度か改良され、矢印計算などの最近の定式化では、わずか5つの法則しか必要としません。[ 3 ]
圏論では、すべてのモナドのクライスリ圏はヒューズ射の真部分集合を形成する。[ 1 ]フレイド圏は一時期射と同等であると考えられていたが、 [ 4 ]その後、射はさらに一般的であることが証明された。射は単に同等であるだけでなく、豊饒化されたフレイド圏と直接等しい。[ 5 ]
すべての型クラスと同様に、矢印は任意のデータ型に適用できる一連の性質と考えることができます。プログラミング言語Haskellでは、矢印によって関数(Haskell ではシンボルで表現) を具体化された->形で組み合わせることができます。ただし、実際の「矢印」という用語は、一部の (すべてではない) 矢印が異なる Kleisli 圏の射(圏論では「矢印」とも呼ばれる)に対応するという事実から来ている可能性もあります。比較的新しい概念であるため、標準的な定義は存在しませんが、すべての定式化は論理的に同等であり、いくつかの必須メソッドを備え、特定の数学法則に厳密に従います。[ 6 ]
Haskellの標準ライブラリ で現在使用されている記述では、基本的な操作は3つしか必要とされない。
arr : ( s -> t ) -> A s tfirst2 つの型間の矢印を受け取り、それをタプル間の矢印に変換するパイプ方式。タプルの最初の要素は、入力と出力のうち変更される部分を表し、2 番目の要素はu、計算をバイパスする変更されない部分を表す3 番目の型です。 [ 7 ]まず: A s t -> A ( s , u ) ( t , u )>>>( >>> ) : A s t -> A t u -> A s u矢印を定義するために厳密に必要な手順はこれら3つだけですが、実践的にも理論的にも矢印をより扱いやすくするための他の方法も導き出すことができます。
もう1つの便利な方法は、arrおよびfirst(そしてからfirst導き出せる)から導き出すことができます。
( *** ) : A s t -> A u v -> A ( s , u ) ( t , v )矢印は、明確に定義された手順を持つことに加えて、適用される可能性のあるあらゆるタイプに対して、特定の規則に従わなければなりません。
arr id == idarr ( f >>> g ) == arr f >>> arr g first ( f >>> g ) == first f >>> first garr ( first f ) == first ( arr f )残りの法則は、合成の順序が逆になった場合のパイピング法の挙動を制限し、式を簡略化することも可能にします。
arr ( id *** g ) >>> first f == first f >>> arr ( id *** g )最初のf >>> arr (( s , t ) -> s ) == arr (( s , t ) -> s ) >>> ffirst ( first f ) >>> arr ( (( s , t ), u ) -> ( s ,( t , u )) ) == arr ( (( s , t ), u ) -> ( s ,( t , u )) ) >>> first f矢印は、追加の操作や制約を定義することで、特定の状況に合わせて拡張できます。よく使われるバージョンには、計算が条件付き決定を行えるようにする選択付き矢印や、ステップが自身の出力を入力として受け取ることができるフィードバック付き矢印などがあります。適用付き矢印と呼ばれる別の矢印のセットは、モナドと同等であるため、実際にはほとんど使用されません。[ 6 ]
アローにはいくつかの利点があり、そのほとんどはプログラムロジックを明示的かつ簡潔にできる能力に由来します。副作用を回避することに加えて、純粋関数型プログラミングは静的コード解析の機会を増やします。これは理論的には、コンパイラの最適化の向上、デバッグの容易化、構文糖衣などの機能につながる可能性があります。[ 6 ]
厳密には矢印を必要とするプログラムはありませんが、矢印は、本来であれば純粋な宣言型コードで必要となるような、多くの複雑な関数受け渡しを一般化します。また、プログラムのステップ間の共通のリンクに独自のクラス定義を与えることで、コードの再利用を促進することもできます。型に汎用的に適用できる機能も再利用性に貢献し、インターフェースをシンプルに保ちます。[ 6 ]
矢印には、矢印の法則を満たす矢印を定義する初期作業など、いくつかの欠点があります。モナドは通常実装が容易であり、矢印の追加機能は不要な場合があるため、モナドを使用する方が好ましい場合が多いです。[ 6 ]関数型プログラミングの多くの構成要素に当てはまるもう 1 つの問題は、矢印を含むコードをコンピュータ命令セット アーキテクチャで使用される命令型プログラミングスタイルに効率的にコンパイルすることです。
純粋関数を持ち上げる関数を定義する必要があるためarr、矢印の適用範囲が制限されます。たとえば、双方向変換は矢印にすることはできません。なぜなら、プログラムが純粋関数とその逆関数を提供する必要があるからですarr。[ 8 ]これはまた、不要な伝播を停止するプッシュベースのリアクティブフレームワークを記述するための矢印の使用も制限します。同様に、ペアを使用して値をタプル化すると、値を再グループ化するための追加のコンビネータが必要になる複雑なコーディングスタイルになり、異なる方法でグループ化された矢印の等価性に関する根本的な疑問が生じます。これらの制限は未解決の問題のままであり、Generalized Arrows [ 8 ]や N-ary FRP [ 9 ]などの拡張機能がこれらの問題を探求しています。
矢印の有用性の多くは、profunctor(関数との事前合成と事後合成のみを必要とする) のようなより一般的なクラスに包含されており、それらは に応用されていますoptics。矢印は本質的には強力なプロファンクターであり、同時に圏でもありますが、法則は若干異なります。