我是 Haskell 的新手,需要帮助。我正在尝试构建一种必须具有某种唯一性的新数据类型,因此我决定使用 UUID 作为唯一标识符:
data MyType = MyType {
uuid :: UUID,
elements :: AnotherType
}
这样,我可以执行以下操作:
instance Eq MyType where
x == y = uuid x == uuid y
x /= y = not (x == y)
问题是所有已知的(对我来说)UUID 生成器都会生成 IO UUID,但是我需要在上面提到的纯代码中使用它。您能否建议是否有任何方法可以从 IO UUID 中提取 UUID,或者是否有更好的方法可以在 Haskell 中执行我需要的操作?谢谢。
更新
感谢所有伟大的建议和代码示例。从这里发布的内容来看,我可以说你不能破坏 referential transparency ,但是有一些聪明的方法可以在不破坏问题的情况下解决问题,并且可能是最理想的方法,列在下面的答案中。
还有一种替代方法,我可以根据所提供的使用 State Monad 的建议来探索自己:
type M = State StdGen
type AnotherType = String
data MyType = MyType {
uuid :: UUID,
elements :: AnotherType
} deriving (Show)
mytype :: AnotherType -> M MyType
mytype x = do
gen <- get
let (val, gen') = random gen
put gen'
return $ MyType val x
main :: IO ()
main = do
state <- getStdGen
let (result, newState) = runState (mytype "Foo") state
putStrLn $ show result
let (result', newState') = runState (mytype "Bar") newState
setStdGen newState'
putStrLn $ show result'
不确定它是否是最优雅的实现,但它有效。
最佳答案
如果您正在查看 uuid
包中的函数,那么 UUID
有一个 Random
实例。这意味着可以使用来自 System.Random
的标准函数使用种子以纯代码生成一系列随机 UUID:
import System.Random
import Data.UUID
someUUIDs :: [UUID]
someUUIDs =
let seed = 123
g0 = mkStdGen seed -- RNG from seed
(u1, g1) = random g0
(u2, g2) = random g1
(u3, g3) = random g2
in [u1,u2,u3]
请注意,每次调用
someUUIDs
时都会创建相同的三个“唯一”UUID,因为种子是硬编码的。与所有纯 Haskell 代码一样,除非您作弊(使用不安全的函数),否则您不能指望在调用
StdGen
之间不显式传递某些状态(在本例中为 random
RNG)的情况下生成一系列实际唯一的 UUID。避免传递生成器的丑陋样板的常用解决方案是在可以维护所需状态的 monad 中运行至少部分代码。有些人喜欢使用
MonadRandom
包,尽管您也可以使用常规的 State
monad 和 StdGen
州中的某个地方。 MonadRandom
相对于 State
的主要优势在于,您可以获得一些专用语法( getRandom
),并且可以创建一个包含 RandomT
和 StateT
的 monad 堆栈,以便您可以将 RNG 应用程序的其余状态与其他状态分开。使用
MonadRandom
,您可能会编写一个应用程序,例如:import Control.Monad.Random.Strict
import System.Random
import Data.UUID
-- monad for the application
type M = Rand StdGen
-- get a generator and run the application in "M"
main :: IO ()
main = do
g <- getStdGen -- get a timestamp-seeded generator
let log = evalRand app g -- run the (pure) application in the monad
putStr log
-- the "pure" application, running in monad "M"
app :: M String
app = do
foo <- myType "foo"
bar <- myType "bar"
-- do some processing
return $ unlines ["Results:", show foo, show bar]
type AnotherType = String
data MyType = MyType {
uuid :: UUID,
elements :: AnotherType
} deriving (Show)
-- smart constructor for MyType with unique UUID
myType :: AnotherType -> M MyType
myType x = MyType <$> getRandom <*> pure x
请注意,应用程序的主要部分需要使用 monad 语法编写并在应用程序
M
monad 中运行。这不是一个很大的限制——大多数非平凡的应用程序将用一些 monad 编写。
关于Haskell UUID 生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58906666/