Haskell UUID 生成

标签 haskell

我是 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 ),并且可以创建一个包含 RandomTStateT 的 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/

相关文章:

haskell - Haskell 书中的编程部分代码无法正常工作

haskell - (>>=) 折叠时的惰性

haskell - 为什么 HashMap 在一系列插入后不处于正常形式?

haskell - 类型注释的中缀优先级是什么 (::)

`otherwise` 函数中的 Haskell 非详尽模式

Haskell QuickCheck2 使用 ByteString?

haskell - 我不明白类型声明

haskell - cabal repl 太慢了

Haskell 继承、数据、构造函数

haskell - 解决模糊类型变量