在工作中已经有一些关于将其作为禁止使用unsafePerformIO和相关的部门范围政策的讨论.就个人而言,我并不介意,因为我一直认为,如果我发现自己想要使用它,通常意味着我需要重新思考我的方法.
这种限制听起来合理吗?我似乎记得在某个地方读到它主要是为了FFI,但是我不记得我现在读到的地方.
编辑:好的,那是我的错.它不会受到合理需要的限制,即.FFI.政策的重点更多是为了阻止懒惰和代码味道.
我正在创建一个Haskell应用程序,它在无限循环上生成一个随机数(仅在客户端请求时).但是,我应该只为此目的使用纯函数.randomIO在unsafeperformIO没有任何严重稳定性或性能风险的情况下包装是否安全?
在这个伪代码块中:
atomically $ do
if valueInLocalStorage key
then readValueFromLocalStorage key
else do
value <- unsafeIOToSTM $ fetchValueFromDatabase key
writeValueToLocalStorage key value
Run Code Online (Sandbox Code Playgroud)
使用安全unsafeIOToSTM吗?文档说:
STM实现通常会多次运行事务,因此如果您的IO有任何副作用,您需要为此做好准备.
基本上,如果事务失败,那是因为某个其他线程,wroteValueToLocalStorage并且当重试事务时,它将返回存储的值,而不是再次从数据库中获取.
STM实现将中止已知无效且需要重新启动的事务.这可能发生在unsafeIOToSTM的中间,因此请确保您没有获取任何需要释放的资源(在中止事务时忽略异常处理程序).例如,这包括使用Handles执行任何IO.出错可能会导致随机死锁.
这让我很担心.从逻辑上讲,如果fetchValueFromDatabase没有打开新连接(即使用现有连接),一切都应该没问题.我还缺少其他陷阱吗?
当IO运行时,事务可能已经看到不一致的内存视图.由于事务的实现方式,在整个程序中您期望为真的不变量在事务中可能不正确.通常这对程序员来说是不可见的,但是使用unsafeIOToSTM可以暴露它.
key 是单个值,没有不变量可以打破.
我开始研究一个将元胞自动机定义为局部转换函数的项目:
newtype Cellular g a = Cellular { delta :: (g -> a) -> a }
Run Code Online (Sandbox Code Playgroud)
无论何时g是a Monoid,都可以通过在应用本地转换之前移动焦点来定义全局转换.这给了我们以下step功能:
step :: Monoid g => Cellular g a -> (g -> a) -> (g -> a)
step cell init g = delta cell $ init . (g <>)
Run Code Online (Sandbox Code Playgroud)
现在,我们可以通过使用简单地运行自动机iterate.通过memo模仿每个步骤,我们可以节省很多(而且我确实意味着很多:它可以节省数小时)重新计算:
run :: (Monoid g, Memoizable g) => Cellular g a -> (g -> a) -> [g -> a]
run cell = iterate (memo . …Run Code Online (Sandbox Code Playgroud) 我找到了一些示例代码,并稍微改了一下
counter = unsafePerform $ newIORef 0
newNode _ = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
Run Code Online (Sandbox Code Playgroud)
每次运行时返回1然后2然后3然后3等.
但是当我改变它
newNode = unsafePerformIO $
do
i <- readIORef counter
writeIORef counter (i+1)
return i
Run Code Online (Sandbox Code Playgroud)
然后我每次运行都得0.
为什么会发生这种情况,我该怎么做才能解决这个问题?
我试图通过添加对"putStrLn"的调用来获取Haskell函数以显示它何时应用:
isPrime2 1 = False
isPrime2 n = do
putStrLn n
null (filter (==0) (map (mod n) (filter isPrime2 [2..(floor (sqrt(fromIntegral (n-1))))])))
Run Code Online (Sandbox Code Playgroud)
(最终目标是证明为什么一个版本的isPrime比另一个版本更有效.)
当我将上面的代码加载到GHCi中时,我收到错误:
无法将预期类型
Bool与实际类型匹配m0 b0
我确定这是一个n00b错误.有人能告诉我正确的方法来完成我想要做的事情吗?
我正在做一个Haskell绑定到图像加载库,我想尽可能避免复制.加载图像时,我从包含图像数据的C库中获取数据结构.现在,这个结构对于所有意图和目的都是不可变的,但是将数据从它读入Haskell是一个IO动作.是否可以使用unsafePerformIO(或者也许是unsafeDupablePerformIO以获得更好的性能)来避免将内存复制到Haskell数组或类似内容中?当然,我需要将数据结构指针封装在ForeignPtr或类似的中,并确保不能以任何其他方式访问或修改指针.
这些案件的惯例是什么?
这是一个后续问题,是否有充分的理由使用unsafePerformIO?
所以我们知道
p_sin(double *p) { return sin(*p); }
Run Code Online (Sandbox Code Playgroud)
不安全,不能用unsafePerformIO.
但该p_sin函数仍然是一个数学函数,它以不安全的方式实现的事实是一个实现细节.我们并不完全希望矩阵乘法在IO中,因为它涉及分配临时内存.
我们如何以安全的方式包装这个功能?我们需要锁定,自己分配内存等吗?是否有处理此问题的指南/教程?
我希望stdGen在没有IO的情况下,在每个调用中返回不同的函数.我尝试使用unsafePerformIO,如下面的代码.
import System.IO.Unsafe
import System.Random
myStdGen :: StdGen
myStdGen = unsafePerformIO getStdGen
Run Code Online (Sandbox Code Playgroud)
但是当我尝试调用myStdGenghci时,我总是得到相同的值.我受虐了unsafePerformIO吗?或者还有其他方法可以实现我的目标吗?
编辑 对不起,我想我应该更准确地描述我的问题.
实际上,我正在实现treap数据strutcure的变体,它需要一个特殊的"合并"操作.它依赖于一些随机性来保证摊销的O(log n)预期时间复杂度.
我试图使用一对像(Tree, StdGen)每个treap保持随机发生器.当向treap插入新数据时,我将使用random随机值给新节点,然后更新我的生成器.但我遇到了一个问题.我有一个函数empty,它将返回一个空的treap,我使用myStdGen上面的函数来获得这个treap的随机生成器.但是,如果我有两个空的treap,它们StdGen将是相同的.因此,在我将数据插入treap并且当我想合并它们之后,它们的随机值也将是相同的.因此,我失去了我依赖的随机性.
这就是为什么我想要一个某种"全局"随机生成器,它StdGen为每次调用产生不同的结果,这样每个空的treap可能会有不同的StdGen.
为什么accursedUnutterablePerformIO(又名inlinePerformIO)实施
accursedUnutterablePerformIO :: IO a -> a
accursedUnutterablePerformIO (IO a) = case a realWorld# of (# _, r #) -> r
Run Code Online (Sandbox Code Playgroud)
并不是
accursedUnutterablePerformIO :: IO a -> a
accursedUnutterablePerformIO (IO a) = case a realWorld# of (# s, r #) -> s `seq` r
Run Code Online (Sandbox Code Playgroud)
?