本文介绍了我滥用unsafePerformIO吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 为了熟悉 unsafePerformIO (如何使用它以及何时使用它),我实现了一个用于生成唯一值的模块。 以下是我的: module Unique(newUnique)其中 导入Data.IORef 导入System.IO.Unsafe(unsafePerformIO) - 键入以表示唯一的事物。 - Show仅用于测试目的。 newtype唯一= U整数派生显示 - 我相信这是Haskell'98派生实例,但 - 我希望是明确的,因为其Eq实例是Unique 中最重要的部分。 实例Eq唯一其中(U x)==(U y)= x == y 计数器:: IORef整数计数器= unsafePerformIO $ newIORef 0 updateCounter :: IO() updateCounter = do x< - readIORef计数器 writeIORef计数器(x + 1) readCounter :: IO Integer readCounter = readIORef counter newUnique':: IO Unique newUnique'= do {x ; writeIORef计数器(x + 1);返回$ U x} newUnique ::() - > Unique newUnique()= unsafePerformIO newUnique' 令我高兴的是,软件包,名为 Data.Unique 选择了和我一样的数据类型;另一方面,他们选择了 newUnique :: IO Unique 类型,但是我希望不在 IO if可能。 这个实现是否危险?是否可能导致GHC改变使用它的程序的语义? 处理 unsafePerformIO 作为编译器的承诺。它说:我保证你可以把这个IO行为看作是一个纯粹的价值,没有什么会出错的。这很有用,因为有时候您可以为使用不纯操作实现的计算构建纯接口,但编译器无法验证这是何时发生的;而不是> unsafePerformIO ,您可以将自己的手放在心上,并发誓 证实不纯的计算实际上是纯粹的,因此编译器可以简单地相信它是。 在这种情况下,承诺是错误的。如果 newUnique 是一个纯函数,那么在(x,x)和 let x = newUnique() c>(newUnique(),newUnique())将是等效的表达式。但是你会希望这两个表达式有不同的结果;在一种情况下具有相同的唯一值的一对重复,并且在另一种情况下具有一对两个不同的独特值。用你的代码,真的没有办法说出任何一种表达的含义。只能通过考虑程序在运行时执行的实际操作顺序来理解它们,并且在使用 unsafePerformIO 时控制它正是您放弃的操作。 unsafePerformIO 表示它无关紧要是否将任一表达式编译为 newUnique 或两个,Haskell的任何实现都可以自由选择任何它喜欢的每一个,每次遇到这样的代码。 To get acquainted with unsafePerformIO (how to use it and when to use it), I've implemented a module for generating unique values.Here's what I have:module Unique (newUnique) whereimport Data.IORefimport System.IO.Unsafe (unsafePerformIO)-- Type to represent a unique thing.-- Show is derived just for testing purposes.newtype Unique = U Integer deriving Show-- I believe this is the Haskell'98 derived instance, but-- I want to be explicit, since its Eq instance is the most-- important part of Unique.instance Eq Unique where (U x) == (U y) = x == ycounter :: IORef Integercounter = unsafePerformIO $ newIORef 0updateCounter :: IO ()updateCounter = do x <- readIORef counter writeIORef counter (x+1)readCounter :: IO IntegerreadCounter = readIORef counternewUnique' :: IO UniquenewUnique' = do { x <- readIORef counter ; writeIORef counter (x+1) ; return $ U x }newUnique :: () -> UniquenewUnique () = unsafePerformIO newUnique'To my delight, the package called Data.Unique chose the same datatype as I did; on the other hand, they chose the type newUnique :: IO Unique, but I want to stay out of IO if possible. Is this implementation dangerous? Could it possibly lead GHC to change the semantics of a program which uses it? 解决方案 Treat unsafePerformIO as a promise to the compiler. It says "I promise that you can treat this IO action as if it were a pure value and nothing will go wrong". It's useful because there are times you can build a pure interface to a computation implemented with impure operations, but it's impossible for the compiler to verify when this is the case; instead unsafePerformIO allows you to put your hand on your heart and swear that you have verified that the impure computation is actually pure, so the compiler can simply trust that it is.In this case that promise is false. If newUnique were a pure function then let x = newUnique () in (x, x) and (newUnique (), newUnique ()) would be equivalent expressions. But you would want these two expressions to have different results; a pair of duplicates of the same Unique value in one case, and a pair of two different Unique values in the other. With your code, there's really no way to say what either expression means. They can only be understood by considering the actual sequence of operations the program will carry out at runtime, and control over that is exactly what you're relinquishing when you use unsafePerformIO. unsafePerformIO says it doesn't matter whether either expression is compiled as one execution of newUnique or two, and any implementation of Haskell is free to choose whatever it likes each and every time it encounters such code. 这篇关于我滥用unsafePerformIO吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-19 21:03