gervais.b
gervais.b

Reputation: 2347

Zio, transform Seq[ZIO] to ZIO[Seq]

That may be a dumb question, but starting with ZIO, I cannot manage to convert a Seq[ZIO] to ZIO[Seq]:

def translate(keys: Seq[String], locales: Seq[Locale]):RIO[Translator, Seq[Translation]] = {
  for {
    service <- ZIO.environment[Translator]
  } yield {
    // service.translate produce a zio.Task[Translation]
    keys.map(k => service.translate(k, locales)
  }
}

Required: RIO[Translator, Seq[Translation]]

Found : ZIO[Translator, Nothing, Seq[zio.Task[Translation]]

I tried flatMap, flatten, collectAll and merge but I was not able to get the expected result with anyone.

How can I transform a Seq[ZIO[_, _, B]] to a ZIO[_, _, Seq[B]] ?

Thanks

Edit: It seems that ZIO.foreach is the best option, however I still have it wrapped inside another ZIO due to the for comprehension.

Upvotes: 3

Views: 2582

Answers (3)

wiki1000
wiki1000

Reputation: 449

To "exchange" List and ZIO you could dance this way:

def dance(x: List[ZIO[Any,Throwable,Int]]): ZIO[Any, Throwable, List[Int]] = 
    x.map ( a => a.map(x=> List(x)))
    .fold ( ZIO.succeed(  List[Int]())  )
        ((x, y) => x.map(a => y.map(b => a ++ b ) )
            .flatten 
        )

Upvotes: 0

francoisr
francoisr

Reputation: 4585

Because for loops translate to flatMap except for the last line which is a map, you want to add the foreach call within the for-loop.

def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = { 
  for {
    translator <- ZIO.environment[Translator]
    translations <- ZIO.foreach(keys)(translator.translate(_, locales))
  } yield translations
}

Upvotes: 11

Boris Azanov
Boris Azanov

Reputation: 4491

If I got you right you can do it using traverse function from cats:

import cats.instances.list._
import cats.syntax.traverse._
import zio.{RIO, Task, ZIO}
import zio.interop.catz._

import java.util.Locale

case class Translation()
trait Translator {
  def translate(k: String, locales: Seq[Locale]): Task[Translation]
}

def translate(keys: Seq[String], locales: Seq[Locale]): RIO[Translator, Seq[Translation]] = {
  val translator: Translator = ???
  for {
    service <- ZIO.effect(translator)
    result  <- keys.toList.traverse(k => service.translate(k, locales))
  } yield result
}

For map List[ZIO[_, _, B]] to ZIO[_, _, List[B]] you can use sequence function and I would advice to use cats library for that.

import zio.ZIO
import zio.interop.catz._
import cats.syntax.traverse._
import cats.instances.list._

def ziosSequence[B](seqZIO: Seq[ZIO[Any, Throwable, B]]): ZIO[Any, Throwable, Seq[B]] = 
  seqZIO.toList.sequence.map(_.toSeq)

the sequence signature is:

def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] =
  traverse(fga)(ga => ga)

Here we see what function do what we need. it requires Applicative instance for G (G is ZIO in your case), and we just import it using import zio.interop.catz._ Also, to make list is able to call sequence we need import Traverse instance for List: by import cats.instances.list._ Unfortunetaly we can not do the same tricks with Seq because we need Traverse instance for sequence, and we should convert Seq to List back and forth before and after sequence.

useful links:

Upvotes: 1

Related Questions