Search code examples
scalazio

How to narrow down ZIO Schedule environment?


There is an example of a simple API that uses ZIO effect to return None or Option[String]. I use ZIO Schedule to run the effect as long the None is returned, but limited to a certain number of times. The example is based on the code from ZIO usecases_scheduling:

import zio._
import zio.random._
import zio.duration._
import zio.console.{Console, putStrLn}
import zio.Schedule

import scala.util.{Random => ScalaUtilRandom}

object RecordAPI {

  def randomId(length: Int): String =
    LazyList.continually(ScalaUtilRandom.nextPrintableChar).filter(_.isLetterOrDigit).take(length).mkString

  def getRecordId: Task[Option[String]] = Task.effect(
    if (ScalaUtilRandom.nextInt(10) >= 7) Some(randomId(16)) else None
  )
}

object ScheduleUtil {

  def schedule[A]: Schedule[Random, Option[String], Option[String]] =
    (Schedule.exponential(10.milliseconds) && Schedule.recurs(10)) *> Schedule.recurWhile(_.isEmpty)
}

object RandomScheduler extends scala.App {
  implicit val rt: Runtime[zio.ZEnv] = Runtime.default

  rt.unsafeRun {
    RecordAPI.getRecordId
      .repeat(ScheduleUtil.schedule)
      .foldM(
        ex => putStrLn(s"failed with ${ex.getMessage}"),
        success => putStrLn(s"Succeeded with $success")
      )
  }
}

This effect below has the type ZIO[Random with clock.Clock, Throwable, Option[String]]:

RecordAPI.getRecordId.repeat(ScheduleUtil.schedule)

I would like to remove the ScheduleUtil.schedule dependency on Random by providing the Random env and to receive the effect ZIO[Any with clock.Clock, Throwable, Option[String]]:

RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(Random))

but I get compilation error:

[error]  found   : zio.random.Random.type
[error]  required: zio.random.Random
[error]     (which expands to)  zio.Has[zio.random.Random.Service]
[error]       .repeat(ScheduleUtil.schedule.provide(Random))
[error]                                             ^
[error] one error found

What parameter should be provided to the .provide method?


Solution

  • Error message talks you that you tries to pass to function provide Random.type in the line:

    RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(Random))
    

    Random is passed as type but provide expects instance of Random. So you can make your code compilable just replacing Random type to some it's instance:

    val hasRandomService: Random = Has.apply(Random.Service.live)
    val randomIdZIO: ZIO[Random, Throwable, Option[String]] = 
      RecordAPI.getRecordId.repeat(ScheduleUtil.schedule.provide(hasRandomService))
    

    but if you want to get rid of ScheduleUtil.schedule maybe it's better to use Schedule.fromFunction function:

    val randomIdZIOFromFunction: ZIO[Random, Throwable, Option[String]] = 
      RecordAPI.getRecordId.repeat(
        Schedule.fromFunction(_ => if (ScalaUtilRandom.nextInt(10) >= 7) Some(randomId(16)) else None)
    )