В настоящее время я пишу привязки к криптографической библиотеке, которая предоставляет функцию для создания пар ключей:
const size_t PUBLICKEYBYTES = 32;
const size_t SECRETKEYBYTES = 32;
int random_keypair(unsigned char pk[PUBLICKEYBYTES],
unsigned char sk[SECRETKEYBYTES]);
Эта функция случайным образом генерирует секретный ключ, вычисляет соответствующий открытый ключ и помещает результаты в pk
и sk
.
Когда я только что вернул один ByteString
, я обнаружил, что проще всего использовать create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString
из Data.ByteString.Internal
. Однако эта функция не может создать два ByteStrings
одновременно.
Мой первый подход состоял в том, чтобы написать что-то вроде:
newtype PublicKey = PublicKey ByteString
newtype SecretKey = SecretKey ByteString
randomKeypair :: IO (PublicKey, SecretKey)
randomKeypair = do
let pk = B.replicate 0 publicKeyBytes
sk = B.replicate 0 secretKeyBytes
B.unsafeUseAsCString pk $ \ppk ->
B.unsafeUseAsCString sk $ \psk ->
c_random_keypair ppk psk
return (PublicKey pk, SecretKey sk)
Однако это не работает с GHC 7.10.2. При запуске набора тестов я обнаружил, что у меня, кажется, есть совместное использование ByteString
s между вызовами функций, что приводит к сбою шифрования/дешифрования и дает неверные результаты.
Мне удалось обойти проблему, определив свою собственную функцию:
createWithResult :: Int -> (Ptr Word8 -> IO a) -> IO (ByteString, a)
createWithResult i f = do
fp <- B.mallocByteString i
r <- withForeignPtr fp f
return (B.fromForeignPtr fp 0 i, r)
и используя его как:
randomKeypair = fmap (PublicKey *** SecretKey) $
createWithResult publicKeyBytes $ \ppk ->
B.create secretKeyBytes $ \psk ->
void $ c_random_keypair ppk psk
Вроде работает, все тесты проходят.
Мой вопрос в том, какова именно семантика, когда речь идет о совместном использовании и ссылочной прозрачности, когда речь идет о монаде IO
?
Моя интуиция подсказывала мне (неверно), что я могу решить задачу первым способом, но, видимо, не смогла. Я полагаю, что произошло то, что оптимизатор увидел, что let
-операторы могут быть помещены в определения верхнего уровня, и это было причиной, по которой у меня возникли эти проблемы.