14

aByteStringは次のコンストラクタであるためForeignPtr:

data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload
                     {-# UNPACK #-} !Int                -- offset
                     {-# UNPACK #-} !Int                -- length

を返す関数がある場合ByteString、定数などの入力が与えられるとWord8、関数は非決定論的な ForeignPtr 値を持つ ByteString を返します-その値が何であるかは、メモリマネージャーによって決定されます。

では、ByteString を返す関数は純粋ではないということですか? ByteString および Vector ライブラリを使用している場合、明らかにそうではないようです。確かに、もしそうなら、広く議論されたでしょう (そしてうまくいけば、Google 検索のトップに表示されます)。その純粋さはどのように強制されますか。

この質問をする理由は、コンストラクターに ForeignPtr メンバーがある場合、GHC コンパイラーの観点から、ByteString および Vector オブジェクトの使用に関連する微妙なポイントは何かに興味があるからです。

4

1 に答える 1

18

モジュールForeignPtrの外部から内部のポインターの値を観察する方法はありません。Data.ByteStringその実装は内部的には純粋ではありませんが、外部的には純粋です。これは、純粋である必要がある不変式が、ByteStringコンストラクター内で見ることができない限り維持されることを保証するためです。

これは Haskell の一般的な手法です。内部で安全でない手法を使用して何かを実装しますが、純粋なインターフェイスを公開します。Haskell の安全性を損なうことなく、安全でない技術がもたらすパフォーマンスとパワーの両方を得ることができます。(もちろん、実装モジュールにはバグがある可能性がありますが、C で記述されていれば、その抽象化がリークする可能性は低いと思いますByteString? :))

微妙な点については、ユーザーの観点から話しているのであれば、心配する必要はありません。ByteString および Vector ライブラリがエクスポートする関数は、. で始まらない限り、心配することなく使用できますunsafe。どちらも非常に成熟しており、十分にテストされたライブラリであるため、純粋性の問題に遭遇することはまったくありません。発生した場合それはライブラリのバグであり、報告する必要があります。

安全でない内部実装で外部の安全性を提供する独自のコードを作成する限り、ルールは非常に単純です:参照の透過性を維持します。

ByteString を例にとると、ByteString を構築する関数はunsafePerformIO、データのブロックを割り当てるために使用し、それを変更してコンストラクターに入れます。コンストラクターをエクスポートした場合、ユーザー コードはForeignPtr. これは問題がありますか?そうであるかどうかを判断するには、この方法で割り当てられた 2 つの ForeignPtr を区別できる純粋な関数 (つまり、 にない関数)を見つける必要があります。ドキュメントIOをざっと見ると、そのような機能があることがわかります。これらを区別できます。したがって、ユーザー コードが. これを行う最も簡単な方法は、コンストラクターをエクスポートしないことです。instance Eq (ForeignPtr a)ForeignPtr

要約すると、安全でないメカニズムを使用して何かを実装する場合は、それによって生成された値を検査するなどして、導入された不純物がモジュールの外に漏れないことを確認してください。

コンパイラの問題に関する限り、実際に心配する必要はありません。関数はunsafeIOですが、そもそもモナドでできることよりも、純粋性に違反することを超えて、より危険なことを行うことを許可するべきではありません。一般に、本当に予想外の結果をもたらす可能性のある何かを実行したい場合は、そのために最善を尽くす必要があります。たとえば、unsafeDupablePerformIO2 つのスレッドが同じサンクを評価する可能性に対処できる場合は、フォームunsafeDupablePerformIO mを同時に。unsafePerformIOよりもわずかに遅いunsafeDupablePerformIOこれを防ぐからです。(あなたのプログラムのサンクは、GHC の通常の実行中に 2 つのスレッドによって同時に評価される可能性があります。同じ純粋な値を 2 回評価しても (定義により) 悪影響はないはずなので、これは通常問題ではありませんが、安全でないコードを記述する場合は、それはあなたが考慮に入れなければならないものです。)

(および、上でリンクした )のGHC ドキュメントにunsafePerformIOunsafeDupablePerformIOは、遭遇する可能性のあるいくつかの落とし穴が詳しく説明されています。同様にunsafeCoerce#(移植可能な名前Unsafe.Coerce.unsafeCoerceで使用する必要があります) のドキュメントも同様です。

于 2011-12-23T14:02:27.740 に答える