import cats.effect.IO
import avocado.*
import avocado.instances.cats.given
val run: IO[Int] =
parallelize {
for {
a <- doStuff1
b <- doStuff2(a)
c <- doStuff3
d <- doStuff4(a)
} yield combine(a, b, c, d)
}avocADO will transform the above for-comprehension to code equivalent to:
for {
a <- doStuff1
(b, c, d) <- doStuff2(a).zip(doStuff3).zip(doStuff4(a))
} yield combine(a, b, c, d)avocADO is a small library that allows for automatic rewriting of for comprehensions to their parallel versions.
The name avocADO is a pun on the function that is the inspiration for this library - ado from Haskell's language extension ApplicativeDo.
libraryDependencies ++= Seq(
"org.virtuslab" %% "avocado" % "version from the badge",
"org.virtuslab" %% "avocado-cats" % "version from the badge", // for Cats
"org.virtuslab" %% "avocado-zio-2" % "version from the badge", // for ZIO 2.x
"org.virtuslab" %% "avocado-zio-1" % "version from the badge", // for ZIO 1.x,
"org.virtuslab" %% "avocado-zio-query" % "version from the badge", // for ZIO Query
)//> using lib "org.virtuslab::avocado:version from the badge"
//> using lib "org.virtuslab::avocado-cats:version from the badge" // for Cats
//> using lib "org.virtuslab::avocado-zio-2:version from the badge" // for ZIO 2.x
//> using lib "org.virtuslab::avocado-zio-1:version from the badge" // for ZIO 1.x
//> using lib "org.virtuslab::avocado-zio-query:version from the badge" // for ZIO QueryAll you need to do in order to use avocADO is to import the parallelize function and an AvocADO instance for your effect system. i.e.
import avocado.* // This line exposes the `parallelize` function - entrypoint of the library
// You should choose one of the following imports depending on your effect system of choice
import avocado.instances.cats.given
import avocado.instances.zio2.given
import avocado.instances.zio1.given
import avocado.instances.zioquery.givenAnd that's it! All that's left is to wrap the for-comprehensions that you want to parallelize with a call to parallelize an watch your program run in parallel! Like so:
parallelize {
for {
...
} yield ...
}On the off chance that avocADO doesn't provide an instance for your favourite effect monad, you might have to write an instance of our AvocADO typeclass yourself. Don't worry, it's relatively straightforward.
AvocADO is just a Monad with a changed name, so that it can be easily associated with avocADO. So if you want to write an instance for a class called Effect it might look like so:
given AvocADO[Effect] = new AvocADO[Effect] {
def pure[A](a: A): Effect[A] = Effect.pure(a)
def map[A, B](fa: Effect[A], f: A => B): Effect[B] = fa.map(f)
def zip[A, B](fa: Effect[A], fb: Effect[B]): Effect[(A, B)] = fa.zipPar(fb) // This is the most important method
def flatMap[A, B](fa: Effect[A], f: A => Effect[B]): Effect[B] = fa.flatMap(f)
}Every parallel part of the computation will be rewritten to a call to zip, so in order to achieve any speedup, you have to provide a parallel implementation for zip.
Inspired by Haskell's Applicative do-notation
Similar project: kitlangton/parallel-for (only for Scala 2)