我正在为我的域模型编写 ScalaCheck 生成器。为了增加灵活性,我的生成器返回函数采用关联的特定值。例如:
case class A(...)
case class B(...)
case class C(a: A, ...)
case class D(b: B, c: C, ...)
val genA: Gen[A] = ???
val genB: Gen[B] = ???
val genC: (A) => Gen[C] = ???
val genD: (B, C) => Gen[D] = ???
这样,我可以选择生成C
与特定A
. 不过,我通常不在乎模型与什么相关联:我想生成C
带有 any的A
. 在这种情况下,我可以执行以下操作之一:
for {
a <- genA
c <- genC(a)
} yield c
// Or, desugared and simplified:
genA flatMap genC
// Or, with a Scalaz Monad for Gen
genA >>= genC
Kleisli(genC) =<< genA
我喜欢后面的选项,因为我可以将它们包含在更复杂的表达式中,而无需相对繁琐的for
循环。D
但是,对于这种需要多个参数的情况,我想不出任何简单的解决方案。看来我被困在了for
:
for {
a <- genA
b <- genB
c <- genC(a)
d <- genD(b, c)
} yield d
底线:
如果有一些干净的语法不仅可以将 Kleisli 箭头和带有n 个参数f: T => M[R]
的“类 Kleisli”函数提升到单子值或. 像这样的东西:g: (T, U) => M[R]
f2: M[T] => M[R]
g2: (M[T], M[U]) => M[R]
// liftX is a hypothetical lifting method enriched onto Function1-N:
genD.liftX(genB, genC.liftX(genA))
我喜欢这种假设方法的原因在于:
- 看起来类似一个未提升的功能应用程序,只是有
liftX
拼接 - 很容易组合成复杂的
genC
表达式genD
我可以写它,但在广阔的Scalaz世界中是否存在这样的东西?如果我对我的多参数函数进行柯里化会有帮助吗?Arrow 组合器会有帮助吗?我一直在浏览 Scalaz 并没有提出任何类似的东西,但我想我会问一下,以防以前有人遇到过这个问题。