3

Basically I am trying to make generators which are the result of HTTP requests, due to this I often end up with types like Gen[EitherT[Future, Error, T]].

The problem is that there doesn't appear to be any monadic instances (so I can do sequence, or monad transformers) that let me compose different instances of Gen[EitherT[Future, Error, T]]

As an example, suppose we have the following functions

def genUser: Gen[EitherT[Future, Error, User]]

and

def genAccounts(user: User): Gen[EitherT[Future, Error, List[Account]]

The idea is to be able to properly compose the Gen[EitherT[Future, Error,T] types, so that genAccounts calls genUser, i.e. something like

def genAccounts(user: User): Gen[EitherT[Future, Error, List[Account]] = for { user <- genUser accounts <- genAccounts(user) } yield accounts

Also does scalacheck Gen provide a way to lift a Future into a Gen (i.e. a way to go from Gen[Future[T]] to just a Gen[T]). Even if this is blocking, its not a huge issue if it only happens once when we generate the final Gen property

mdedetrich
  • 1,899
  • 1
  • 18
  • 29

1 Answers1

0

As of the moment (late 2022) there seems to be no native support on the ScalaCheck side for effectful generators. There was one issue regarding asynchronous Props. As is mentioned in the issue, the solution is now implemented in the library scalacheck-effect.

The above functionality could be a workaround, but would require nesting asynchronous properties rather than generating the data directly, e.g.

import org.scalacheck.Gen
import org.scalacheck.effect.PropF

import java.util.UUID
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{ Await, Future }

object TestAsync {

  case class Box(uuid: UUID)
  case class Wrapped(box: Box, name: Option[String])

  val propF: PropF[Future] = PropF.forAllF(Gen.uuid) { uuid =>
    Future(Box(uuid)).map { box =>
      PropF.forAllF(Gen.alphaNumStr) { name =>
        Future(Wrapped(box, Some(name))).map { wrapped =>
          PropF.boolean[Future](wrapped.name.nonEmpty)
        }
      }
    }
  }

  def main(args: Array[String]): Unit = {
    Await.result(
      propF
        .check()
        .map(println),
      Duration.Inf
    )
  }

}

The use case is only for demonstration and already shows that using nested PropFs is somewhat involved.

Another option may be to use ZIO, which handles values very similar to EitherT[Future, Error, A], and has effectful properties.

Nikita
  • 41
  • 4