Lift Kleisli-like function to take monadic values as arguments

423 Views Asked by At

I'm writing ScalaCheck generators for my domain models. For added flexibility, my generator-returning functions take specific values for the associations. For example:

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] = ???

This way, I have the option of generating arbitrary C's that are associated with a specific A. Often, though, I don't care what the model is associated with: I want to generate a C with any A. In that case, I can do one of these:

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

I like having the latter options because I can include them in more complicated expressions without the relatively cumbersome for loop. However, I can't come up with any simple solutions for cases like D that take more than one argument. It seems I'm stuck with the for:

for {
  a <- genA
  b <- genB
  c <- genC(a)
  d <- genD(b, c)
} yield d

Bottom Line:

I would really appreciate it if there was some clean syntax for lifting not only a Kleisli arrow like f: T => M[R], but also a "Kleisli-like" function with n arguments g: (T, U) => M[R], onto a function over monadic values f2: M[T] => M[R] or g2: (M[T], M[U]) => M[R]. Something like this:

// liftX is a hypothetical lifting method enriched onto Function1-N:
genD.liftX(genB, genC.liftX(genA))

What I like about this hypothetical method is that it:

  • looks similar an unlifted function application, just with the liftX spliced in
  • is easy to compose into a complicated expression (see the lifting of genC inside the lifting of genD)

I could write it, but does something like this exist in the vast world of Scalaz? Would it help if I curried my multi-argument functions? Would Arrow combinators help? I've been browsing Scalaz for a while and haven't come up with anything similar, but I thought I'd ask in case anyone has run into this before.

0

There are 0 best solutions below