让我们说有所有可能的事情清单
all3PStrategies :: [Strategy3P]
all3PStrategies = [strategyA, strategyB, strategyC, strategyD] //could be longer, maybe even infinite, but this is good enough for demonstrating
现在我们有另一个函数采用整数N
和两个策略,并使用第一个策略N
次,然后使用第二个策略N
次并继续只要需要重复。
如果N
为0会发生什么,我想返回一个随机策略,因为它打破了函数的目的,但它必须最终应用特定的策略。
rotatingStrategy [] [] _ = chooseRandom all3PStrategies
rotatingStrategy strategy3P1 strategy3P2 N =
| … // other code for what really happens
所以我试图从列表中获得一个rondom策略。我认为这样做会:
chooseRandom :: [a] -> RVar a
但是如何使用Haddock / doctest测试呢?
-- >>> chooseRandom all3PStrategies
-- // What goes here since I cant gurauntee what will be returned...?
我认为随机函数有点违背了Haskell的功能概念,但我也可能错了。在命令式语言中,随机函数使用各种参数(如Java中的时间)来确定随机数,所以我不能只插入一个特定的参数来确保我得到哪个随机数?
答案 0 :(得分:2)
如果您这样做:chooseRandom :: [a] -> RVar a
,那么您将无法使用IO
。您需要能够在整个类型声明中包含IO
monad,包括测试用例。
更明确地说,只要您使用IO
monad,所有返回类型都必须包含IO
monad的类型,该类型不太可能包含在您想要返回的列表中,除非您编辑列表的结构以容纳包含IO
类型的项目。
答案 1 :(得分:1)
有几种方法可以实现chooseRandom
。如果您使用的是返回RVar Strategy3P
的版本,则仍需要使用RVar
对runRVar
进行抽样,以获得可以实际执行的Strategy3P
。
你也可以使用IO
monad来解决这个问题,这实际上并没有什么不同:我们可以将chooseRandom
视为返回我们可以根据需要进行采样的概率分布的函数,而不是将其视为一个函数,它返回一个我们可以根据需要进行评估的计算。根据您的观点,这可能会使事情或多或少地混乱,但至少它避免了安装rvar包的需要。使用chooseRandom
的{{1}}的一个实现是pick
function from this blog post:
IO
这段代码可以说是错误的:当你给它空列表时,它会在运行时崩溃。如果您对此感到担心,可以通过将结果包装在import Random (randomRIO)
pick :: [a] -> IO a
pick xs = randomRIO (0, (length xs - 1)) >>= return . (xs !!)
中来检测编译时的错误,但如果您知道您的策略列表永远不会为空(例如,因为它是硬编码的),那么它可能不值得打扰。
可能接下来它也不值得测试,但是对于基本问题有许多解决方案,即如何测试monadic函数。换句话说,给定一个monadic值Maybe
,我们如何在我们的测试框架中查询它(理想情况下通过重用对原始值m a
起作用的函数)?这是QuickCheck库及相关研究论文Testing Monadic Code with QuickCheck)中解决的复杂问题。
然而,看起来将QuickCheck与doctest集成起来并不容易,问题实在太简单了,无法证明投资一个全新的测试框架!鉴于您只需要一些快速而肮脏的测试代码(实际上并不是您的应用程序的一部分),在这里使用a
可能没问题,即使许多Haskeller认为它是代码味道:< / p>
unsafePerformIO
请确保您理解为什么使用{-|
>>> let xs = ["cat", "dog", "fish"]
>>> elem (unsafePerformIO $ pick xs) xs
True
-}
pick :: [a] -> IO a
“不安全”(一般来说这是非确定性的),以及为什么它对于这种情况并不重要(因为标准RNG的失败不是''对于这个应用程序来说,确实有足够大的风险来证明我们需要在类型系统中捕获它所需的额外工作。