2013-02-20 10 views
37

nタプルは、ネストされた2タプルの束で表現できることは明らかです。ではなぜ、彼らはハスケルで同じものではないのですか?これは何かを壊すだろうか?なぜ(a、(b、(c、(d、())))))のための砂糖ではないのですか?

これらの型を同等にすると、タプルに関数を書くのがずっと簡単になります。たとえば、zip、zip2、zip3などを定義する代わりに、すべてのタプルに対して機能する単一のzip関数のみを定義できます。もちろん

、あなたできネストされた2つのタプルを持つ作品が、それは醜いですとネスティングを実行するためのいかなる標準的な方法はありません(すなわちはずの私たちの巣左または右?)。

+0

http://stackoverflow.com/questions/14621559/naryary-tuples-vs-pairs –

答えて

35

タイプ(a,b,c,d)は、(a,(b,(c,(d,()))))とは異なるパフォーマンスプロファイルを持ちます。一般に、nタプルへのインデックス付けはO(1)をとりますが、n個のネストされたタプルの「hlist」へのインデックス付けはO(n)です。

これは、Olegの古典的な研究をHListsで調べる必要があります。 HListを使用するには、広範で、ややスケッチしたタイプレベルのプログラミングが必要です。多くの人々はこれを容認できず、ハスケルの初期には利用できませんでした。おそらく今日HListを表現するための最良の方法は、これは標準的なネストを与えるGADTsとDataKinds

data HList ls where 
    Nil :: HList '[] 
    Cons :: x -> HList xs -> HList (x ': xs) 

であり、そしてあなたは、このタイプのすべてのインスタンスのために働く関数を記述することができます。 printfで使用されているのと同じ手法を使用して、マルチウェイzipWithを実装することができます。もっと興味深いのは、このタイプの適切なレンズを作成することです(ヒント:インデックス作成のためにタイプレベルのナチュラルとタイプファミリーを使用する)。

私は、配列を使ったHListのようなライブラリと、一般的なインターフェイスに固執しながらパフォーマンスのようなタプルを得るためにフードの下でunsafeCoerceを書くことを考えました。私はそれをしていないが、それは過度に困難ではありません。

編集:このことについてもっと考えてみると、私は時間があるときに何か一緒にハックすることになります。反復コピーの問題Andreas Rossbergの言及はおそらく、ストリーム融合または類似の技術を使用して排除することができます。

+0

私はオレグの仕事を見ており、それが私にこの基本的な考え方をもたらしました。彼のライブラリ(および私が見たすべての変種)の構文は、実際には使用するのがひどいです。また、ネストされたタプルがO(n)のパフォーマンスヒットを起こすことを認識しませんでした。 O(1)を生成するためにコンパイラがネスト解除を行うことはできませんでしたか? –

+0

時間をコンパイルするのではなく、実行時にパフォーマンスについて話していると思っていました。ネストされたタプルのコンパイル時のパフォーマンスはそれほど良くないとは思いますが、それは大きな問題のようには見えません。 –

+0

@MikeIzbicki私はランタイムパフォーマンスについて話していました。原理的には、おそらく多くの時間にタプルにインライン展開される可能性がありますが、現在利用可能な種類のコンパイラ機構はありません。 C++のテンプレートは "unboxed"で使用できるので、タプルのC++実装は、あなたが望むやり方で完全に汎用的であることに注意してください。 GHCでは、多形フィールドをunboxすることはできませんので、これを行うことはできません(したがって、配列を使用するライブラリは、内部的に安全でない拘束を解決するソリューションです)。 –

23

ハスケルの主な問題は、ネストされたタプルが怠惰のために追加の値を許可することです。たとえば、タイプ(a,(b,())にはすべて(x,_|_)または(x,(y,_|_))が存在しますが、これはフラットタプルの場合には当てはまりません。これらの値の存在は意味的に不便であるだけでなく、タプルの最適化をはるかに難しくします。

厳密な言葉で言えば、あなたの提案は確かに可能性があります。しかし、それでもパフォーマンスの落とし穴が発生します。実装ではまだタプルを平坦化したいと考えています。そのため、誘導的に実際に構築または分解する場合は、繰り返しコピーする必要があります。実際に大きなタプルを使用すると、問題が発生する可能性があります。

+3

入れ子になったタプルをフラット化したものと同形、 '(a、!(b、!(c、!()))))〜(a、b、c)'?また、実行時間の代わりにコンパイル時にすべてのコピーを実行できるわけではありませんか? –

+1

@MikeIzbicki、AFAICT、Haskellに ''(a、!(...))型のようなものはありません。データ型コンストラクタのパラメータには、厳密なものとしてのみ注釈を付けることができます。コピーに関しては、タプルリテラルを書き留める場所は必要ありませんが、誘導的に*を組み立てたり分解したりするとき、つまりその長さに渡って再帰を行うと、避けられません。 –

+2

タプルの現在の表現を保持する方法はあるのでしょうか?入れ子にされたタプルに似たインタフェースを提供しています。基本的には、タプルは同じ方法で動作しますが、再帰的な型クラスインスタンスなどのタプルに対して汎用コードを書くのも簡単です。 –

関連する問題