Java/Scala/C++言語でのジェネリッククラス/関数のポリモーフィズムなどのパラメトリックポリモーフィズムとHaskell型システムでの「アドホック」ポリモーフィズムの主な違いを理解したいと思います。私は第1種の言語に精通していますが、Haskellを使用したことがありません。
より正確に:
前もって感謝します。
[〜#〜] tapl [〜#〜] に従って、§23.2:
パラメトリックポリモーフィズム(...)を使用すると、実際の型の代わりに変数を使用して1つのコードを「一般的に」型指定し、必要に応じて特定の型でインスタンス化できます。パラメトリック定義は均一です。それらのインスタンスはすべて同じように動作します。 (...)
対照的に、アドホックポリモーフィズムは、ポリモーフィック値が異なるタイプで「表示」されたときに異なる動作を示すことを可能にします。アドホックなポリモーフィズムの最も一般的な例はオーバーロードです。これは、単一の関数シンボルを多くの実装に関連付けます。コンパイラー(または、オーバーロードの解決が静的か動的かによって、ランタイムシステム)は、引数のタイプに基づいて、関数の各アプリケーションに適切な実装を選択します。
したがって、歴史の連続する段階を考慮すると、非一般的な公式Java(別名プレ( J2SE 5. 、2004年9月以降))にはアドホックなポリモーフィズムがありました。 メソッドのオーバーロード -ただし、パラメトリックなポリモーフィズムではないため、 一般的なメソッドを記述する はできません。その後、もちろん両方を行うことができます。
比較すると、その最初から 1990年 であるHaskellはパラメトリックに多態的であり、次のように記述できます。
swap :: (A; B) -> (B; A)
swap (x; y) = (y; x)
ここで、AとBは型変数です。変数は、仮定なしにall型にインスタンス化できます。
しかし、ad-hocポリモーフィズムを与える既存の構成要素はありませんでした。これは、severalに適用される関数を記述できるようにしますが、すべてではないタイプ。タイプクラスは、この目標を達成する方法として実装されました。
class(Javaインターフェースに似たもの)を記述し、型シグネチャジェネリック型に実装したい関数。次に、このクラスに一致するいくつかの(そしてうまくいけばseveral)instancesを登録できます。それまでの間、次のような一般的なメソッドを記述できます。
between :: (Ord a) a -> a -> a -> Bool
between x y z = x ≤ y ^ y ≤ z
ここで、Ord
は、関数(_ ≤ _)
を定義するクラスです。使用すると、(between "abc" "d" "ghi")
は静的に解決されて文字列に適切なインスタンスが選択されます(整数などではなく)-正確に(Javaの)メソッドがオーバーロードされる瞬間。
Javaでも バインドされたワイルドカード を使用して同様のことができます。しかし、その前のHaskellとJavaの主な違いは、Haskellだけが自動的に辞書を渡すことができるということです:両方の言語でOrd T
の2つのインスタンスを指定すると、b0
とb1
を使用すると、これらを引数として受け取り、ペアのタイプ(b0, b1)
のインスタンスを、たとえば辞書式順序を使用して生成するf
関数を作成できます。 (("hello", 2), ((3, "hi"), 5))
が与えられたとしましょう。 Javaでは、string
をそのオブジェクトに適用するために、int
およびf
のインスタンスを覚えて、正しいインスタンス(between
の4つのアプリケーションで構成される)を渡す必要があります。 Haskellは compositionality を適用し、グラウンドインスタンスとf
コンストラクター(これはもちろん他のコンストラクターにも拡張されます)のみを指定して正しいインスタンスを構築する方法を理解できます。
今、type inferenceに関する限り(そしてこれはおそらく別の質問になるはずです)、両方の言語でincomplete、コンパイラがタイプを判別できないn-annotatedプログラムを常に記述できるという意味で。
haskellの場合、これは impredicative (a.k.a. first-class)ポリモーフィズムがあり、型推論が決定できないためです。その点で、Javaは1次多型(Scalaが展開するもの)に限定されることに注意してください。
javaの場合、これは 反変サブタイプ をサポートするためです。
しかし、これらの言語は主に、実際の型推論が適用されるプログラムステートメントの範囲と、型推論の結果の正確性の重要性が異なります。
Haskellの場合、推論はすべての「高度にポリモーフィックではない」用語に適用され、よく知られているアルゴリズムの公開された拡張に基づいて健全な結果を返すように真剣に取り組みます。
A
およびB
)は、non-polymorphic型でのみインスタンス化できます(単純化していますが、これは基本的にMLスタイルのポリモーフィズムです)たとえばOcamlで)。Javaの場合、型推論はずっと限定的な方法で適用されますとにかく:
Java 5のリリース前は、Javaでの型推論はありませんでした。 Java言語カルチャによると、すべての変数、メソッド、および動的に割り当てられたオブジェクトのタイプは、プログラマーが明示的に宣言する必要があります。ジェネリック(クラスごとにパラメーター化されたクラスとメソッド)がJava 5に導入されたとき、言語は、変数、メソッド、および割り当てに関するこの要件を保持しました。しかし、ポリモーフィックメソッド(タイプごとにパラメーター化)の導入により、(i)プログラマーがすべてのポリモーフィックメソッド呼び出しサイトでメソッドタイプ引数を提供するか、または(ii)言語がメソッドタイプ引数の推論をサポートするかが決まりました。プログラマーに追加の事務的負担をかけないようにするために、Java 5の設計者は、型推論を実行して型引数を決定することを選択しましたポリモーフィックメソッド呼び出しの場合。 ( ソース 、強調鉱山)
推論アルゴリズム は本質的に GJのそれ ですが、 somewhatkludgy の後にワイルドカードが追加されています(注ただし、J2SE 6.0で行われた可能な修正については最新ではありません)。アプローチの概念的な大きな違いは、Javaの推論はlocalであり、式の推論された型は型システムから生成された制約とその型にのみ依存するという意味です部分式、ただしコンテキスト上ではありません。
不完全で、時には正しくない型の推論に関するパーティーラインは、比較的くつろいでいることに注意してください。 仕様 に従って:
また、型推論はどのようにも健全性に影響を与えないことにも注意してください。推論された型が無意味な場合、呼び出しにより型エラーが発生します。型推論アルゴリズムは、実際によく機能するように設計された、ヒューリスティックと見なす必要があります。望ましい結果を推測できない場合は、代わりに明示的な型のパラメーターを使用できます。
Parametric polymorphismは、型を気にせず、どの型にも同じ関数を実装することを意味します。たとえば、Haskellの場合:
length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + length xs
リストの要素のタイプが何であるかは関係ありません。いくつあるかは関係ありません。
Ad-hoc polymorphism(aka method overloading)ただし、パラメーターのタイプに応じて異なる実装を使用することを意味します。
Haskellの例を示します。 makeBreakfast
という関数を定義するとします。
入力パラメータがEggs
の場合、makeBreakfast
に卵の作り方に関するメッセージを返してほしい。
入力パラメーターがPancakes
の場合、makeBreakfast
にパンケーキの作成方法に関するメッセージを返してほしい。
BreakfastFood
関数を実装するmakeBreakfast
というタイプクラスを作成します。 makeBreakfast
の実装は、makeBreakfast
への入力のタイプに応じて異なります。
class BreakfastFood food where
makeBreakfast :: food -> String
instance BreakfastFood Eggs where
makeBreakfast = "First crack 'em, then fry 'em"
instance BreakfastFood Toast where
makeBreakfast = "Put bread in the toaster until brown"
John Mitchellのプログラミング言語の概念によれば、
パラメトリックポリモーフィズムとオーバーロード(別名アドホックポリモーフィズム)の主な違いは、パラメータポリモーフィック関数が1つのアルゴリズムを使用してさまざまな型の引数を操作するのに対し、オーバーロード関数は引数のタイプごとに異なるアルゴリズムを使用する可能性があることです。
パラメトリック多態性とその場限りの多態性の意味と、それらがHaskellとJavaで利用できる範囲)についての完全な議論は長めですが、具体的な質問にはもっと簡単に取り組むことができます。
型推論のアルゴリズムの例Java Haskellの型推論との違いは?
私の知る限り、Javaは型推論を行いません。そのため、Haskellがそれを行うという違いがあります。
何かをJava/Scalaで書くことができるがHaskellで書くことができない(これらのプラットフォームのモジュラー機能にもよる)状況、およびその逆の例を教えてください。
Javaできないのは、Haskellができることの非常に単純な例の1つです。maxBound :: Bounded a => a
を定義することはできません。Java Haskellができないことを実行できます。