コンピュータ サイエンスでは、型クラスは、アドホック ポリモーフィズムをサポートする型システム構造です。これは、パラメトリック ポリモーフィック型の型変数に制約を追加することで実現されます。このような制約には通常、型クラスと型変数が含まれ、 は、に関連付けられたオーバーロードされた操作をメンバーがサポートする型にのみインスタンス化できることを意味します。
T aaT
型クラスは、最初にフィリップ・ワドラーとスティーブン・ブロットによってStandard MLの「eqtypes」の拡張として提案された後、プログラミング言語Haskellで初めて実装されました。 [1] [2]もともとは、オーバーロードされた算術演算子と等価演算子を原則的に実装する方法として考えられていました。 [3] [2] Standard MLの「eqtypes」とは対照的に、Haskellで型クラスを使用して等価演算子をオーバーロードする場合、コンパイラのフロントエンドや基礎となる型システムを大幅に変更する必要はありません。[4]
概要
型クラスは、クラスに属するすべての型に対して存在する必要がある関数名または定数名のセットとそれぞれの型を指定することによって定義されます。Haskell では、型をパラメーター化できます。Eq等式を許容する型を含むことを目的とした型クラスは、次のように宣言されます。
クラスEq a where ( == ) :: a -> a -> Bool ( /= ) :: a -> a -> Bool
ここで、 はa型クラス の 1 つのインスタンスでありEq、a2 つの関数 (等価関数と不等価関数) の関数シグネチャを定義します。各関数は、 型の 2 つの引数を受け取りa、ブール値を返します。
型変数は種類(最新のグラスゴー・ハスケル・コンパイラ(GHC)リリースでは としても知られています)aを持ちます[5]。つまり、 の種類は
TypeEq
Eq ::タイプ->制約
この宣言は、「適切な型の、および という名前の関数が定義されている場合、型はa型クラスに属します」と述べていると解釈できます。プログラマーは、次のように関数(要素がリスト内にあるかどうかを判定する関数) を定義できます。
Eq(==)(/=)elem
elem :: Eq a => a -> [ a ] -> Bool elem y [] = False elem y ( x : xs ) = ( x == y ) ||要素y xs
関数はコンテキストをelem持つ型を持ち、その型の範囲を型クラスに属する型に制限します。(Haskell は「クラス制約」と呼ぶことができます。)
a -> [a] -> BoolEq aaaEq =>
特定の型 のすべてのメソッドの実装を定義するインスタンス宣言を使用することで、任意の型tを特定の型クラスのメンバーにすることができます。たとえば、新しいデータ型が定義されている場合、型 の値に対して任意の有用な方法で等価関数を提供することで、この新しい型を のインスタンスにすることができます。これが完了すると、関数は、つまり型 の要素のリストで使用できるようになります。
CCttEqtelem[t]t
型クラスは、オブジェクト指向プログラミング言語のクラスとは異なります。特に、は型ではありません。型 の値というものは存在しません。
EqEq
型クラスは、パラメトリック多態性と密接に関連しています。たとえば、上記で指定した の型は、型クラス制約 " " がなければ、elemパラメトリック多態性型になります。
a -> [a] -> BoolEq a =>
高次の多態性
型クラスは、種類 Typeの型変数を取る必要はなく、任意の種類の型変数を取ることができます。より高次の種類を持つこれらの型クラスは、コンストラクタ クラスと呼ばれることもあります (ここで参照されるコンストラクタはMaybe、 などのデータ コンストラクタではなく、 などの型コンストラクタですJust)。次のクラスがその例ですMonad。
クラスMonad m戻り値:: a -> m a ( >>= ) :: m a -> ( a -> m b ) -> m b
m が型変数に適用されることはType -> Type、それが種類 を持つことを示します。つまり、型を受け取り、型を返すので、 の種類はMonad次のようになります。
モナド:: (型->型) ->制約
マルチパラメータ型クラス
型クラスは複数の型パラメータを許可しているため、型クラスは型の関係として見ることができます。[6]たとえば、GHC 標準ライブラリでは、クラスはIArray一般的な不変配列インターフェースを表現します。このクラスでは、型クラス制約は、が型 の要素を含む配列型であるIArray a eことを意味します。(この多態性に対する制約は、たとえば
ボックス化されていない配列型を実装するために使用されます。)ae
マルチメソッドと同様に、[要出典]マルチパラメータ型クラスは、複数の引数の型、さらには戻り値の型に応じて、メソッドの異なる実装の呼び出しをサポートします。 マルチパラメータ型クラスでは、実行時に呼び出すメソッドを毎回検索する必要はなく、[7] : 分 25:12 むしろ、呼び出すメソッドは、単一パラメータ型クラスと同様に、最初にコンパイルされ、型クラスインスタンスの辞書に格納されます。
マルチパラメータ型クラスを使用する Haskell コードは、この機能が Haskell 98 標準の一部ではないため、移植性がありません。一般的な Haskell 実装である GHC とHugs は、マルチパラメータ型クラスをサポートしています。
機能的依存関係
Haskellでは、型クラスが改良され、プログラマが型パラメータ間の機能的依存性を宣言できるようになりました。これは、リレーショナルデータベース理論からヒントを得た概念です。 [8] [9]つまり、プログラマは、型パラメータのサブセットの特定の割り当てによって、残りの型パラメータが一意に決定されることを主張できます。たとえば、型の状態パラメータを持つ一般的なモナドは、 型クラス制約を満たします。この制約には、機能的依存性があります。これは、型クラス の特定のモナドについて、 からアクセスできる状態型が一意に決定されることを意味します。これは、コンパイラの型推論に役立つだけでなく、プログラマの型指向プログラミングにも役立ちます。
msMonad.State s mm -> smMonad.Statem
サイモン・ペイトン・ジョーンズは、複雑さを理由にHaskellにおける関数依存性の導入に反対している。[10]
型クラスと暗黙のパラメータ
型クラスと暗黙のパラメータは本質的に非常に似ていますが、まったく同じではありません。次のような型クラス制約を持つ多態的関数:
合計::数値a => [ a ] -> a
は、暗黙的に のインスタンスを受け入れる関数として直感的に扱うことができますNum。
合計_ :: Num_ a -> [ a ] -> a
インスタンスはNum_ a、本質的には のインスタンス定義を含むレコードですNum a。(これは実際に、Glasgow Haskell コンパイラによって型クラスが内部的に実装される方法です。)
しかし、決定的な違いがあります。暗黙のパラメータはより柔軟で、異なるインスタンスをNum Int渡すことができます。対照的に、型クラスは、いわゆる一貫性プロパティを強制します。これは、特定の型に対してインスタンスの選択肢が 1 つだけ存在することを要求するものです。一貫性プロパティにより、型クラスは多少反モジュール化されるため、孤立したインスタンス (クラスも対象の型も含まないモジュールで定義されたインスタンス) は強く推奨されません。しかし、一貫性は言語に別のレベルの安全性を追加し、同じコードの 2 つの分離した部分が同じインスタンスを共有することを保証します。[11]
たとえば、順序付きセット(型Set a) が機能するには、要素 (型 ) に完全な順序付けが必要です。これは、要素の比較演算子を定義する制約 によって証明できます。ただし、完全な順序付けを強制する方法は多数あります。セット アルゴリズムは一般に、セットが構築された後の順序付けの変更を許容しないため、セットを操作する関数に の互換性のないインスタンスを渡すと、誤った結果 (またはクラッシュ) が発生する可能性があります。したがって、この特定のシナリオでは の一貫性を強制することが重要です。
aOrd aOrd aOrd a
Scala型クラスのインスタンス (または「辞書」) は、完全に別の種類のエンティティではなく、言語内の通常の値にすぎません。[12] [13]これらのインスタンスは、明示的に宣言された暗黙の形式パラメータの暗黙のパラメータとして使用するために、スコープ内で適切なインスタンスを見つけることによってデフォルトで提供されますが、通常の値であるということは、あいまいさを解決するために明示的に提供できることを意味します。その結果、Scala 型クラスは一貫性プロパティを満たさず、事実上、暗黙のパラメータの 構文糖衣となります。
これはCatsのドキュメントから抜粋した例です: [14]
// テキスト表現を提供する型クラス
trait Show [ A ] { def show ( f : A ): String }
// 暗黙的なShow[A] インスタンスが利用可能な
場合にのみ機能する多態的関数def log [ A ]( a : A )( implicit s : Show [ A ]) = println ( s . show ( a ))
// String のインスタンス
implicit val stringShow = new Show [ String ] { def show ( s : String ) = s }
// パラメータ stringShow はコンパイラによって挿入されました。
scala > log ( "a string" ) a string
Coq(バージョン8.2以降)も適切なインスタンスを推論することで型クラスをサポートしています。[15] Agda 2の最近のバージョンでも、「インスタンス引数」と呼ばれる同様の機能が提供されています。[16]
演算子オーバーロードの他のアプローチ
Standard MLでは、「等価型」のメカニズムは Haskell の組み込み型クラスにほぼ相当しますEqが、すべての等価演算子はコンパイラによって自動的に導出されます。プログラマによるプロセスの制御は、構造内のどの型コンポーネントが等価型であるか、および多態型内のどの型変数が等価型の範囲であるかを指定することに限られます。
SMLとOCamlのモジュールと関数はHaskellの型クラスと同様の役割を果たすことができますが、主な違いは型推論の役割であり、これにより型クラスはアドホック多態性に適しています。[17] OCaml のオブジェクト指向サブセットは、型クラスのアプローチと多少比較できるもう1つのアプローチです。
関連する概念
オーバーロードされたデータ( GHCで実装)の類似の概念は型ファミリです。[18]
C++、特にC++20では、 Concepts (C++)を使用した型クラスのサポートがあります。例として、上記の型クラスEqのHaskellの例は次のように記述されます。
テンプレート< typename T > concept Equal = requires ( T a , T b ) { { a == b } -> std :: convertible_to < bool > ; { a != b } -> std :: convertible_to < bool > ; };
Cleanでは、型クラスは Haskell に似ていますが、構文が若干異なります。
Rustは一貫性のある型クラスの限定された形式であるトレイトをサポートしています。 [19]
Mercury には型クラスがありますが、Haskell とまったく同じではありません。[さらなる説明が必要]
Scalaでは、型クラスは、暗黙のパラメータなどの既存の言語機能を使用して実装できるプログラミングイディオムであり、それ自体は別の言語機能ではありません。Scala での実装方法により、あいまいな場合、コード内の特定の場所の型にどの型クラスインスタンスを使用するかを明示的に指定できます。ただし、あいまいな型クラスインスタンスはエラーが発生しやすいため、これは必ずしも利点ではありません。
証明支援ツールCoqも、最近のバージョンでは型クラスをサポートしています。通常のプログラミング言語とは異なり、Coq では、型クラスの定義内で記述される型クラスの法則 (モナド法則など) は、使用する前に各型クラスのインスタンスごとに数学的に証明する必要があります。
参考文献
- ^ Morris, John G. (2013). 型クラスとインスタンスチェーン: リレーショナルアプローチ(PDF) (PhD). ポートランド州立大学コンピューターサイエンス学部。doi :10.15760/etd.1010 。
- ^ ab Wadler, P.; Blott, S. (1989). 「アドホック ポリモーフィズムをアドホックにしない方法」。第16 回 ACM SIGPLAN-SIGACT プログラミング言語の原理に関するシンポジウム (POPL '89) の議事録。Association for Computing Machinery。pp. 60–76。doi : 10.1145 /75277.75283。ISBN 0897912942. S2CID 15327197。
- ^ Kaes, Stefan (1988 年 3 月)。「多態的プログラミング言語におけるパラメトリック オーバーロード」。第 2 回ヨーロッパ プログラミング言語シンポジウムの議事録。doi : 10.1007 /3-540-19027-9_9。
- ^ Appel, AW; MacQueen, DB (1991). 「ニュージャージー州の標準 ML」。Maluszyński, J.; Wirsing, M. (編)。プログラミング言語の実装とロジックプログラミング。PLILP 1991。コンピュータサイエンスの講義ノート。第 528 巻。Springer。pp. 1–13。CiteSeerX 10.1.1.55.9444。doi : 10.1007 / 3-540-54444-5_83。ISBN 3-540-54444-5。
- ^ この型はグラスゴーHaskellコンパイラ
Data.Kindのバージョン8で登場した。 - ^ Haskell のページMultiParamTypeClasses。
- ^ GHC では、C Core は Girard & Reynold の System F 型シグネチャを使用して、最適化フェーズでの処理のための型付きケースを識別します。 -- Simon Peyton-Jones「Into the Core - Squeezing Haskell into Nine Constructors」Erlang User Conference、2016 年 9 月 14 日
- ^ Jones, Mark P. (2000). 「機能的依存関係を持つ型クラス」。Smolka, G. (編) 著。プログラミング言語とシステム。ESOP 2000。コンピュータサイエンスの講義ノート。第 1782 巻。Springer。pp. 230–244。CiteSeerX 10.1.1.26.7153。doi :10.1007 / 3-540-46425-5_15。ISBN 3-540-46425-5。
- ^ HaskellのページFunctionalDependencies。
- ^ Peyton Jones、Simon (2006)。「MPTC と機能的依存性」。Haskell -prime メーリング リスト。
- ^ Kmett, Edward (2015年1月21日). Type Classes vs. the World (ビデオ). Boston Haskell Meetup. 2021年12月21日時点のオリジナルよりアーカイブ。
- ^ Oliveira, Bruno CdS; Moors, Adriaan; Odersky, Martin (2010). 「オブジェクトと暗黙の型クラス」( PDF )。ACM 国際オブジェクト指向プログラミングシステム言語およびアプリケーション会議 (OOPSLA '10) の議事録。Association for Computing Machinery。pp. 341–360。CiteSeerX 10.1.1.205.2737。doi : 10.1145 / 1869459.1869489。ISBN 9781450302036. S2CID 207183083。
- ^ 「Scala初心者ガイド パート12: 型クラス - Daniel Westheide」。
- ^ typelevel.org、Scala 猫
- ^ Castéran, P.; Sozeau, M. (2014). 「Coq の型クラスと関係の簡単な入門」(PDF) . CiteSeerX 10.1.1.422.8091 .
- ^ 「インスタンス引数を使用した型クラスのモデリング」。
- ^ Dreyer, Derek; Harper, Robert; Chakravarty, Manuel MT (2007)。「モジュラー型クラス」。第 34 回 ACM SIGPLAN-SIGACT プログラミング言語の原理に関するシンポジウム (POPL '07) の議事録。63 ~ 70 ページ。63 ページを参照。doi : 10.1145 /1190216.1190229。ISBN 978-1595935755. S2CID 1828213. TR-2006-03.
- ^ 「GHC/型ファミリー - HaskellWiki」.
- ^ Turon, Aaron (2017). 特殊化、一貫性、API の進化 (レポート).
- Peyton Jones, Simon 、 Jones, Mark、Meijer, Erik (1997 年 5 月)。「型クラス: 設計空間の探究」。Proc . ACM SIGPLAN Haskell Workshop。CiteSeerX 10.1.1.1085.8703。
外部リンク
- 「5. 型クラスとオーバーロード」。Haskellの簡単な入門。2000 年 6 月。バージョン 98。
- ユトレヒト大学の高度な関数型プログラミング コース、高度な型クラスに関する 74 枚の講義スライド。2005-06-07。
- 型クラスの実装と理解。2014-11-13。
