GHC 8.2リリース

事実上の標準Haskellコンパイラの新しいバージョン-GHC 8.2.1! このリリースは反復的な改良ですが、同時に、コードの記述の利便性、表現力のある言語、コンパイルされたプログラムのパフォーマンスに関連する興味深い新機能も多数備えています。 私の意見では、最も興味深い変更について考えてみましょう!

コンパクトな地域


パフォーマンスに直接影響する変更の1つです。 特定のデータセットを1つの大きなオブジェクト(領域)としてマークし、ガベージコレクター(コンパクト)を1回実行し、この領域内に少なくとも1つのリンクがある限り、内部に登ってオブジェクトグラフに沿って実行しないで生きていると見なすことができます後続のアセンブリで。

これは、たとえば、作業の最初の段階でプログラムが大きなデータセットを作成し、その後のほとんどの期間で使用する場合に役立ちます。 たとえば、 公式の説明では、例としてガベージコレクション時間が1.5倍のスペルチェッカーの辞書を挙げており、一部のテストではGCに費やされる時間が2〜3倍短縮されています。 正式なロジックと実装の説明を書いた紙張りの人は(おそらく若干より総合的なベンチマークで)いくつかのクレイジーな数値(p。9、チャート7〜8)を導きます。ここで、ゲインは時々桁違いであり、Haskell GCはそのような生産準備を追い越し始めます-GCが隠されたOracle JVMのようなモンスター。

これを使用するのは非常に簡単です。特定の値から領域を作成するには、 Data.Compactモジュールの関数compact :: a -> IO (Compact a) Data.Compact compact :: a -> IO (Compact a)Data.Compactます。その後、 getCompact :: Compact a -> a 。 合計すると、次のようになります。

 compacted <- getCompact <$> compact someBigHeavyData 

当然、コンパクトな領域を作成する場合、オブジェクトはほぼ完全に計算されます(より具体的には、領域が閉じていることを証明するのに十分です)。したがって、たとえば、無限リストを圧縮することはお勧めできません。

さらに、結果のコンパクト領域をシリアル化および逆シリアル化できます。 確かに、予約あり:シリアル化解除プログラムは、一般に、アドレス空間まで、シリアル化プログラムとまったく同じである必要があります。そのため、ASLRでさえすべてを壊します。

フランクリンへのちょっとした言及
上記の記事を注意深く読むと、compactclass compactable aが記事に追加され、 compact関数のシグネチャがCompactable a => a -> IO (Compact a)ます。 実際のAPIでは、この定数は存在せず、関数のドキュメントのエントリには、領域などに可変データがある場合、互換性のないコアが例外をスローすることが記載されています。 したがって、この場合、著者は使いやすさのためにタイプセーフを犠牲にしているようです。

導出戦略


GHCには、クラスのインスタンスクラスを推測するための少なくとも3つ半のメカニズムがあります。

1.標準クラス( ShowReadEq )およびGHCがそれ自体を出力できるクラス(すべての種類のFunctorおよびTraversable 、ならびにDataTypeableおよびGeneric )の出力。

2. DeriveAnyClass拡張により有効化されたデフォルトのメソッド実装を介した出力

この場合、発表
 {-# LANGUAGE DeriveAnyClass #-} class Foo a where doFoo :: a -> b doFoo = defaultImplementation data Bar = Bar deriving(Foo) 

で行われます

 data Bar = Bar instance Foo Bar 

FooがGenericsメカニズム(たとえば、 Aesonによる JSONやCassavaによるCSVへの変換のインスタンスなど)を通じて表示できる場合、またはFooの最小限の定義がメソッドをまったく必要Fooない場合(よりアカデミックな記述を作成する場合に便利)タイムクラスが定理の条件の証人として使用される場合のコード)。

3. newtypeを介して作成された型のエイリアスの場合、 GeneralizedNewtypeDeriving拡張機能を介してベース型のtypclassesの実装を直接使用することもできます。

 {-# LANGUAGE GeneralizedNewtypeDeriving #-} newtype WrappedInt = WrappedInt { unwrap :: Int } deriving(Unbox) 

したがって、問題は、GHC 8.2より前では、複数の拡張機能を一度に有効にするとどのメカニズムを使用するかを指定することができなかったことです-たとえば、 DeriveAnyClassGeneralizedNewtypeDeriving同時に有効にすると、最初の拡張が優先されます。実際、同じモジュールで両方の拡張機能を使用できませんでした。

今、あなたは書くことができます

 {-# LANGUAGE DeriveAnyClass, GeneralizedNewtypeDeriving, DerivingStrategies #-} newtype Baz = Baz Quux deriving (Eq, Ord) deriving stock (Read, Show) deriving newtype (Num, Floating) deriving anyclass C 

スタンドアロンの派生宣言で​​戦略を指定できます。

 data Foo = Foo deriving anyclass instance C Foo 

興味深いことに、以前のバージョンでは{-# #-}を使用することが提案されていましたが、最終的には上記のアプローチが実装されました。

その他のインスタンスの自動拡張


DeriveAnyClass賢明。 第一に、署名*または* -> *持つ時間クラスに制限されなくなりました。 次に、デフォルトの実装定数からインスタンス制約が導出されます。 そのため、たとえば、このようなコードはこれまでに追加されていません。

 {-# LANGUAGE DeriveAnyClass, DefaultSignatures #-} class Foo a where bar :: a -> String default bar :: Show a => a -> String bar = show baz :: a -> a -> Bool default baz :: Ord a => a -> a -> Bool baz xy = compare xy == EQ data Option a = None | Some a deriving (Eq, Ord, Show, Foo) 

Fooのインスタンスには定数(Ord a, Show a)がなく、コンパイラーはそれらを手動で追加することを提案したためです。 これで、対応する定数が出力インスタンスに自動的に追加されます。

GeneralizedNewtypeDerivingも考案されました。 場合によっては(実際、実際に興味深いもののほとんどで)、タイプクラスに関連付けられたタイプも自動的に表示されます。 例えば型の場合

 class HasRing a where type Ring a newtype L1Norm a = L1Norm a deriving HasRing 

コンパイラはインスタンスを生成します

 instance HasRing (L1Norm a) where type Ring (L1Norm a) = Ring a 

バックパック


現在、OCaml信者はHaskellistsをトロールする理由がわずかに少なくなっています:GHC 8.2は、以前よりもはるかに高度なモジュールシステムを導入しました-バックパック。 これ自体はかなり大きく複雑な変更であり、別の記事にふさわしいので、正式な説明と短い例で 論文著者の論文を参照します。

その他


選択した他の変更をリストします。

Source: https://habr.com/ru/post/J334028/


All Articles