从非单子函数中获取随机数

Getting random numbers from non-monadic functions

所以我的目标是能够调用一个非 monadic 函数并让它 return 一个随机值。

getNums :: [Int] -- this only works when the type signature is "IO [Int]"
getNums = getListFromIO 10

getListFromIO :: Int -> IO [Int]
getListFromIO n = do
  gen <- newStdGen
  return $ generateList n gen

generateList :: Int -> StdGen -> [Int]
generateList n gen = take n $ randomRs (1,9) $ gen

如果我调用getListFromIO,一切正常;我得到了我珍贵的随机整数列表,每次都不一样。但是每个调用它的函数都必须使用 IO [Int] 它的类型签名。我不要那个。

我如何构造它以便我能够获得类型为 [Int] 的随机数列表?

IO的目的就是不允许在纯代码中产生副作用。 IO monad 中的所有非纯代码都必须 运行。随机性也基于状态,或从外部资源(全局状态)读取。因此,如果没有黑客攻击,您的尝试是不可能的。我强烈建议您不要使用任何技巧。这种黑客攻击完全绕过了类型系统提供的安全性;它们应该仅限于本地化的安全黑客,主要是当类型系统不够智能并且需要一些说服力时。但这里的情况并非如此——让一个纯函数故意表现得像一个 non-pure 函数在很大程度上不是本地化的 hack,因为任何调用它的代码都会继承破碎的语义,无法跟踪纯度结束的位置和non-purity 开始。

最好重新设计代码,不需要在纯代码中生成随机数。只需从位于 IO 中的代码层次结构中的高层点传递随机性。

你不能,不应该,也不需要这样做。

您声称所有使用 getNums :: IO [Int] 的函数都必须在其类型中包含 IO。这是不正确的。您可以使用 IO [Int] 操作以多种方式计算的 [Int] 值,例如:

main = do
  ints <- getNums -- ints is now of type [Int]
  return (map (+1) ints) -- map (+1) does not have IO in its type.

这是引入 FunctorApplicativeMonad 类型类的原因之一:提供一致的方式来处理诸如 IO [Int] 之类的事情当您想避免在任何地方处理 IO 时。