否则的话是单子的

Monadic if else

我是 Haskell 的新手,想生成一棵 Arbitrary 树。
所以我的第一个想法是创建一个任意布尔值,如果它是真的那么 return 一棵空树,否则创建一个非空树:

instance (Arbitrary a) => Arbitrary (BinaryTree a)
  arbitrary = do
    createNonEmpty <- arbitrary
    if createNonEmpty 
      then return Nil
      else generateNonEmptyTree

但是这种创建 bool 并将其用于 if 的模式似乎有点奇怪,感觉应该有一种更惯用的方法。
标准库中是否已经有某种我可以使用的 "monadic if"

arbitrary = ifM arbitrary (return Nil) (generateNonEmptyTree)

或者还有什么最惯用的方法来解决这个问题?

我对 "use once binding" 的一般解决方案是 -XLambdaCase:

instance (Arbitrary a) => Arbitrary (BinaryTree a)
  arbitrary = arbitrary >>= \case
    True  -> return Nil
    False -> generateNonEmptyTree

或者,您可以使用

bool :: a -> a -> Bool -> a
bool f _ False = f
bool _ t True = t

(Bool 等同于 eitherfoldr)

instance (Arbitrary a) => Arbitrary (BinaryTree a)
  arbitrary = bool generateNonEmptyTree (return Nil) =<< arbitrary

特别是对于 QuickCheck,我会使用 oneof:

arbitrary = oneof [return Nil, generateNonEmptyTree]

它基本上完成了您在问题中提出的建议(生成一个 one-off 值,然后立即使用它):

oneof :: [Gen a] -> Gen a
oneof [] = error "QuickCheck.oneof used with empty list"
oneof gs = choose (0,length gs - 1) >>= (gs !!)

但由于它是一个库函数,这意味着您不必在自己的代码中查看 one-off 值。