ハスケルはすべて抽象に関するものです。しかし、抽象化は、ヒープ上のすべての抽象(多型)データの共通表現のために余分な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
でポリモフィックでないことを意味します)。そのような場合のベストプラクティスは何ですか?
ランタイム情報について:haskellには完全な依存型がありません。 Haskellは、実行時の値と型を区別しています。そのため、Vecの長さは消去されることが保証されています(長さはランタイム値ではなく、実行時に型が保持されるためです)。 – russoulmc