2012-01-22 6 views
2

私はHaskellを呼び出すC関数を持っています。 C関数はHaskellデータ型のStablePtrを渡し、Haskellコードはその値の一部を変更する必要があります。これを行う効率的な方法は何ですか?たとえば、次のようHaskell + StablePtr

foreign export ccall editChar :: StablePtr MyObject -> CInt -> CChar -> IO() 

data MyObject = Obj String 

editChar :: StablePtr MyObject -> CInt -> CChar -> IO() 
editChar cMyObjectPtr index newChar = do 
    -- Code goes here 

はどのようにeditCharはインデックスにnewCharにでシャアを設定するために、可能な限り効率的かつHaskellyように実施される検討しますか?最終的には、変更されるオブジェクトはかなり大きなメモリになり、多くのサブコンポーネントを持つため、editCharの結果として新しいオブジェクトを返すことは問題になりません。

答えて

4

の中にCharを変更することはできません。実際、StablePtrの内容をまったく変更することはできません。 StablePtrを逆参照してMyObjectを元に戻すだけで済みます。

あなたは

newtype MyObject = Obj (IORef String) 

(またはMVarの代わりIORef)を定義するなら、あなたはいつもの方法でそれを変異させることができます。

メモリ消費が懸念される場合は、Stringはまったく適していません。それは5 machine words per characterを使用します。しかし、 "新しい値を返す"というオーバーヘッドは思ったほど高くはないかもしれません。共有のおかげで、は、文字列全体をコピーするのではなく、古いものへの参照を再利用するだけです。StringCharを追加すると、 。 Seqなどのツリー構造では、これらの利点は要素の置き換えにも引き継がれます。

しかし、突然変異をたくさんしている場合は、可変値vectorを考慮する必要があります。

もちろん、Stringが単なる例である場合(最後の段落が示すように)、このアドバイスは必ずしも当てはまるとは限りません。しかし、あなたは

data Huge = Huge { giganticPart :: Gigantic, smallPart :: Int } 

を持っているならば、myHuge { smallPart = 42 }Gigantic全体をコピーするつもりはない、とGiganticは適切なツリー構造であれば、あなたは全部をコピーせずに、それへの変更を行うことができるでしょう。これは、純粋に機能的で永続的なデータ構造の中心的なアイデアであり、Haskellの最も重要な利点の1つです。