gpt4 book ai didi

haskell - ghci 可以重新编码 unsafePerformIO IO block 中的 IO 操作吗

转载 作者:行者123 更新时间:2023-12-02 16:05:26 26 4
gpt4 key购买 nike

可以对 unsafePerformIO 内调用的 IO block 中的 IO 操作重新排序吗?

我有效地拥有了 IO 功能。

assembleInsts :: ... -> IO S.ByteString
assembleInsts ... = do
tmpInputFile <- generateUniqueTmpFile
writeFile tmpInputFile str
(ec,out,err) <- readProcessWithExitCode asm_exe [tmpInputFile] ""
-- asm generates binary output in tmpOutputFile
removeFile tmpInputFile
let tmpOutputFile = replaceExtension tmpIsaFile "bits" -- assembler creates this
bs <- S.readFile tmpOutputFile -- fails due to tmpOutputFile not existing
removeFile tmpOutputFile
return bs

其中S.ByteString是严格的字节字符串。

遗憾的是,我需要在远离 IO monad 的纯代码树中调用它,但由于我汇编器的行为是引用透明的(给定独特的文件)工具,我想暂时我可以制作暂时是一个不安全的接口(interface)。

{-# NOINLINE assembleInstsUnsafe #-}
assembleInstsUnsafe :: ... -> S.ByteString
assembleInstsUnsafe args = unsafePerformIO (assembleInsts args)

此外,我在模块顶部添加了以下注释按照文档(System.IO.Unsafe)的说明。

{-# OPTIONS -fno-cse #-}
module Gen.IsaAsm where

(我也尝试添加 -fnofull-laziness ,根据引用文献我咨询过,但是这个被编译器拒绝了。我不这么认为不过这里也适用这种情况。)

ghci 中运行它报告以下错误。

*** Exception: C:\Users\trbauer\AppData\Local\Temp\tempfile_13516_0.dat: openBinaryFile: does not exist (No such file or directory)

但是如果我删除removeFile tmpOutputFile,那么它就会神奇地起作用。因此,removeFile 似乎是在进程终止之前执行的。这可能吗?字节串是严格的,我什至尝试用a强制在某一点输出:

S.length bs `seq` return ()

removeFile之前。

有没有办法转储中间代码以找出发生了什么?(也许我可以用进程监视器或其他东西来追踪这一点。)不幸的是,我想在此操作中进行清理(删除文件)。

我认为 exe 版本可能可以工作,但在 ghci 下它会失败(已解释)。我正在使用上一个 Haskell 平台的 GHC 7.6.3。

我知道 unsafePerformIO 是一个非常大的锤子,并且具有与之相关的其他风险,但它确实会限制我的软件更改的复杂性。

最佳答案

这可能不适用,因为它基于您的问题中未指定的假设。特别是,这个答案基于以下两个假设。未指定的 SData.ByteString.Lazy,未定义的 tmpDatFiletmpOutputFile

import qualified Data.ByteString.Lazy as S
...
let tmpDatFile = tmpOutputFile

可能的原因

如果这些假设成立,即使不使用 unsafePerformIOremoveFile 也会过早运行。代码如下

import System.Directory
import qualified Data.ByteString.Lazy as S

assembleInsts = do
-- prepare a file, like asm might have generated
let tmpOutputFile = "dataFile.txt"
writeFile tmpOutputFile "a bit of text"
-- read the prepared file
let tmpDatFile = tmpOutputFile
bs <- S.readFile tmpOutputFile
removeFile tmpDatFile
return bs

main = do
bs <- assembleInsts
print bs

导致错误

lazyIOfail.hs: DeleteFile "dataFile.txt": permission denied (The process cannot access the file because it is being used by another process.)

删除行 removeFile tmpDatFile 将使此代码正确执行,就像您所描述的那样,但留下临时文件并不是我们想要的。

可能的解决方案

将导入 S 更改为

import qualified Data.ByteString as S

结果是正确的输出,

"a bit of text".

说明

Data.ByteSting.LazyreadFile 的文档指出它将

Read an entire file lazily into a ByteString. The Handle will be held open until EOF is encountered.

在内部,readfile 通过调用 unsafeInterleaveIO 来完成此操作。 unsafeInterleaveIO 推迟 IO 代码的执行,直到评估它返回的项。

hGetContentsN :: Int -> Handle -> IO ByteString
hGetContentsN k h = lazyRead -- TODO close on exceptions
where
lazyRead = unsafeInterleaveIO loop

loop = do
c <- S.hGetSome h k -- only blocks if there is no data available
if S.null c
then do hClose h >> return Empty
else do cs <- lazyRead
return (Chunk c cs)

因为在上例中定义的 bs 的构造函数被 print 编辑之前,没有任何东西会尝试查看它,而这种情况直到 removeFile 之后才会发生 已执行,在执行 removeFile 之前,不会从文件中读取任何 block (且文件未关闭)。因此,当执行removeFile时,readFile打开的Handle仍然处于打开状态,并且无法删除该文件。

关于haskell - ghci 可以重新编码 unsafePerformIO IO block 中的 IO 操作吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25027293/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com