2017-10-13 22 views
0

ハスケルはすべて抽象に関するものです。しかし、抽象化は、ヒープ上のすべての抽象(多型)データの共通表現のために余分なCPUサイクルと余分なメモリ使用を必要とします。高性能の要求で抽象コードをよりうまく演奏させるためのいくつかの方法があります。私が理解する限り、それが行われる一つの方法は特殊化です - 基本的に余分なコード生成(マニュアルまたはコンパイラ)、正しい?Haskell(GHC)専門ツアー&効率的なタイプファミリー

(コンパイラはより多くの最適化を行うことができますどの?)のは、以下のすべてのコードが厳格であると仮定しよう

我々は機能sum持ちの場合:

sum :: (Num a) => a -> a -> a 

を私たちはspecializeを使用しての特殊なバージョンを生成することができますプラグマ:

{-#SPECIALIZE sum :: Float -> Float -> Float#-} 

これで、もしhaskellコンパイラがコンパイル時に、sumを2つのFloatに呼び出すことを決定すると、その特殊なバージョンを使用します。ヒープ割り当てはありません

機能が完了しました。同じプラグマをクラスインスタンスに適用できます。ロジックはここで変わらないでしょうか?

しかし、データ型はどうですか? こちらはTypeFamiliesが担当していると思いますか?

従属する長さインデックスのリストを特化しましょう。

--UVec for unboxed vector 
class UVec a where 
    data Vec (n :: Nat) a :: * 

instance UVec Float where 
    data Vec n Float where 
    VNilFloat :: Vec 0 Float 
    VConsFloat :: {-#UNPACK#-}Float -> 
        Vec n Float -> 
        Vec (N :+ 1) Float 

しかし、Vecに問題があります。 の各インスタンスがUVecという同じコンストラクタを持つVecを提供する必要はありません。これにより、Vecの各インスタンスに対してVecの各関数を実装する必要があります(パターンマッチングの欠如は、Vecでポリモフィックでないことを意味します)。そのような場合のベストプラクティスは何ですか?

答えて

1

あなたが言うように、UVec aに一致するパターンは一致しません。aはわかりません。 1つのオプションは、ベクトルクラスをカスタム関数で拡張する別の型クラスを使用することです。

class UVec a => UVecSum a where 
    sum :: UVec a -> a 

instance UVecSum Float where 
    sum = ... -- use pattern match here 

後に、場合、我々はv :: UVec Float、我々はインスタンスで定義されたFloat固有のコードが呼び出されますsum vを使用しています。

0

部分的な回答ですが、おそらくそれが役に立ちます。

私が理解する限り、それは専門化です - 基本的に余分なコード生成(マニュアルまたはコンパイラ)、正しい?

はい、これはC++テンプレートのコードインスタンス化に似ています。

コンパイル時に、2つの浮動小数点数の合計を呼び出すことができるようになりました。特殊なバージョンを使用します。ヒープ割り当てはありません

はい可能であれば、コンパイラは特殊バージョンを呼び出します。ヒープ割り当てに関して何を意味するのかは不明です。

依存型ベクトルについては、通常は(私はIdrisから知っています)、ベクトルの長さは可能な限りコンパイラによって排除されます。より強力な型チェックを目的としています。実行時に、長さ情報は役に立たず、削除することができます。

+1

ランタイム情報について:haskellには完全な依存型がありません。 Haskellは、実行時の値と型を区別しています。そのため、Vecの長さは消去されることが保証されています(長さはランタイム値ではなく、実行時に型が保持されるためです)。 – russoulmc

関連する問題