2015-09-28 25 views
6

私はこの段落(http://www.seas.upenn.edu/~cis194/lectures/02-lists.html)に出くわしたとき、私はHaskellの上の講義ノートを読んでいた。ハスケルでタイプ消去?

をこの「思いやりではないが、」パラメトリック多型手段でどのような「パラメトリック」です。すべてのHaskell関数は型パラメータでパラメトリックでなければなりません。機能はこれらのパラメータの選択に基づいて気にしたり決定したりしてはなりません。 aがintのときには関数は1つのことを実行できず、aがBoolのときには別のことはできません。 Haskellはこのような操作を書くための機能を提供していません。この属性はパラメトリシティと呼ばれます。

パラメトリックの多くの深刻で深刻な結果があります。 1つの結果はタイプ消去と呼ばれるものです。実行中のHaskellプログラムは型情報に基づいて決定を下すことができないため、コンパイル時にすべての型情報を削除することができます。 Haskellコードを書くときの重要なタイプにもかかわらず、Haskellコードを実行すると完全に無関係です。このプロパティは、実行時に型を保持する必要のある他の言語(Pythonなど)と比較して、Haskellに大きなスピードを与えます。 (タイプ消去が速くハスケルを作る唯一のものではありませんが、Haskellは時々、Pythonのより速い20倍でクロックされる。)

は、「すべてのHaskellの関数」パラメトリックどのように私は理解していないとは何ですか? Haskellでは型が明示的/静的ではありませんか?また、タイプ消去がどのように改善するのか、実際には理解していません コンパイル時間 ランタイム?

申し訳ありませんが、これらの質問が本当に基本的なものであれば、私はハスケルが初めてです。

EDIT:

もう一つ質問:なぜ著者は「Haskellのコードを書いているとき、どのように重要なタイプにもかかわらず、Haskellのコードを実行するとき、彼らは完全に無関係である」と言うのでしょうか?

+2

「タイプ消去はコンパイル時間を向上させる」とは決して言いませんが、それは単に_runtime_の改善に役立ちます。 – chi

+0

関数はパラメトリックです。型パラメータ 'a'を持つ場合、実行時にその型に基づいて' a'を検査し決断を下すことはできません。したがって、 'map'のような関数はすべての入力リストに対して一様に動作するので、実行時にリスト要素の型を維持する必要はありません。これをC#のような言語と比較すると、リフレクションによって実行時に実際のリストのパラメータタイプを取得できます。 – Lee

答えて

13

「すべてのHaskell関数」はどのようにパラメトリックであるのですか?

これは、すべてのHaskellの関数は、パラメトリックある言っていない、それは言う:

すべてのHaskellの関数は、その型パラメータでパラメトリックでなければなりません。

ハスケル関数には型パラメータは必要ありません。

もう一つ質問:なぜ著者は「Haskellのコードを書いているとき、どのように重要なタイプにもかかわらず、Haskellのコードを実行するとき、彼らは完全に無関係である」と言うのでしょうか?あなたは(たとえば)二つのものが一緒にそれらを追加しようとする前に番号がある場合は、実行時にチェックする必要があります動的型付け言語とは異なり

は、あなたの実行中のHaskellのプログラムは、あなたがそれらを一緒に追加しようとしている場合ことを知っていますコンパイラがあらかじめそれを確認しているので、数値でなければなりません。

はHaskellでは静的/明示的なタイプではありませんか?

ハスケルの型はしばしば推論できます。その場合、それらは明示的にする必要はありません。しかし、静的であることは間違いありません。なぜなら、静的な意味は、コンパイラがプログラムの実行前にすべての型を確実にするためです。

2

通常、動的に型指定された言語の実装では、各値とともに型情報をメモリに格納する必要があります。これはLispのような言語にはあまりにも悪いことではなく、いくつかの型しかなく、いくつかのタグビットで合理的にそれらを識別することができます(ただし、そのような限られた型は他の効率の問題につながります)。多くの種類の言語の方がはるかに悪いです。 Haskellはタイプ情報をランタイムに運ぶことを可能にしますが、それを明示的に指定する必要があるため、コストに注意を払うことができます。たとえば、コンテキストにTypeable aを追加すると、そのタイプの値は実行時にタイプaの表現にアクセスします。微妙には、typeclassインスタンス辞書は通常、コンパイル時に特化されていますが、十分に多相で複雑なケースでは、実行時に生き残る可能性があります。見かけ上放棄されたJHCのようなコンパイラや、まだ開始されていないコンパイラTHCの可能性のあるものでは、これはポインタ型の形式でランタイムに漏れるいくつかの型情報につながる可能性があります。しかし、これらの状況は非常に識別しやすく、重大なパフォーマンス上の問題を引き起こすことはめったにありません。

8

コンパイル時に(Trueのように)式の型がわかっているか、実行時にその型が問題ではないため(など)、型はHaskellで消去できます。このかかわらへの警告があります

、それはすべての値が均一な表現のいくつかの種類を持っていることを前提としています。ほとんどのHaskellの実装はすべてのポインタを使用するので、ポインタが指しているものの実際の型は問題ではありません(ガベージコレクタを除いて)。しかし、一様ではない表現を使用するHaskellの実装を想像してから、守らなければならない。

5

その他は、すでに答えているが、おそらくいくつかの例は助けることができます。

>>> def f(x): 
... if type(x)==type(0): 
...  return (x+1,x) 
... else: 
...  return (x,x) 
... 
>>> f("hello") 
('hello', 'hello') 
>>> f(10) 
(11, 10) 

上記関数、xタイプintである場合を除き、対(x,x)を返すx任意引数、を与え:

Pythonは、例えば、実行時までの情報を入力し保持します。機能の実行時にそのタイプのテスト、およびx場合は、それが代わりに(x+1, x)を返す、特別な方法で振る舞うintであることが判明しています。

上記を実現するために、Pythonランタイムは種類を追跡する必要があります。私たちは、Pythonは単にメモリに5のバイト表現を格納することはできません

>>> x = 5 

を行う場合には、あります。また、タイプタグintでその表現をマークする必要があります。そうすれば、type(x)タグを復元することができます。 などの操作を行う前に、Pythonはtypeタグをチェックして、実際にintを処理していることを確認する必要があります。 xが例えばstr ingの場合、Pythonは例外を送出します。

は静的Javaなどの言語では、実行時にこのようなチェックを必要としないにチェック。例えば、我々は、コンパイラがすでに確認されてい

SomeClass x = new SomeClass(42); 
x.foo(); 

実行したときに実際にコンパイル時にxための方法fooがありますので、再びそれをする必要はありません。これにより、原則としてパフォーマンスが向上します。 Pythonはありませんように、それは持っているので(実際には、JVMは、クラスのロード時にいくつかのランタイムチェックを行いますが、のは、簡略化のためにそれらを無視しましょう)上記にもかかわらず

を、Javaのは、店舗型のタグにを持っていますtype(-)類似:従って

if (x instanceof SomeClass) { ... 

、Java(登録商標)は、1つのいくつかのタイプの「特別」振る舞うことができる機能を記述することを可能にします。

// this is a "generic" function, using a type parameter A 
<A> A foo(A x) { 
    if (x instanceof B) { // B is some arbitrary class 
     B b = (B) x; 
     return (A) new B(b.get()+1); 
    } else { 
     return x; 
    } 
} 

上記の機能foo()はちょうどそれが新しいオブジェクトが代わりに作成されたタイプBのだ場合を除き、その引数を返します。これは、instanceofを使用した結果、実行時にすべてのオブジェクトがタグを運ぶ必要があります。

実際、このようなタグは仮想メソッドを実装できるように既に存在しているので、それ以上の費用はかかりません。しかし、instanceofが存在すると、タイプに対して上記の不均一な振る舞いを引き起こすことが可能になります。いくつかのタイプは異なって扱うことができます。

ハスケルには、そのようなtype/instanceof演算子がありません。タイプ

foo :: a -> (a,a) 

すべてのタイプで同じように動作しなければなりません持つパラメトリックHaskellの機能。いくつかの "特別な"動作を引き起こす方法はありません。具体的には、foo x return (x,x)でなければなりません。これは上記の型注釈を見るだけです。ポイントを強調するために、そのような特性を証明するためにコード(!!)を見る必要はありません。これは何ですかパラメトリック上記のタイプから確実です。

+0

Javaの例はあまりよく分かりません。最初のインスタンスでは、 'foo'を呼び出すのは一般的にランタイム型のチェックを伴いますが、' SomeClass'のいくつかのサブクラスで 'foo'メソッドがオーバーライドされているかどうかを判断する必要があるからです。そして、第二に、 'A'へのキャストは三面的に間違っています。あなたは(消去のために)型パラメータの型にキャストできません。その型は完全に知られているので、コンストラクタ呼び出しをキャストするための使用は決してありません。このコンテキストでは、 'B'は明らかに' A'のサブクラスなので、とにかくキャストする必要はありません。 – amalloy

+0

@amalloy 1)私は完全にはわかりませんが、vtableへのアクセスとそのメソッドの呼び出しは、何かをチェックする必要はなく、盲目的にポインタをたどるだけだと思います。 2) '(A)'キャストなしでコードをコンパイルしてみてください。コンパイラは 'B'がジェネリック' A'のサブタイプではないと正当に訴えます。 (ただし、実行時のチェックされていないキャストについての警告があります) – chi

+0

公正な点について(1)私は推測します。あなたは(2)について真実ですが、あなたの答えを実際にはサポートしていません。実行時にキャストが起こることはまったくありません。バイトコード操作がゼロになるようにコンパイルされます(または、おそらく、Objectへのキャスト、基本的にはノーオペレーションです)。コンパイラを幸せにするためにコンパイル時には必要ですが、実行時には何もしません。 – amalloy

関連する問題