ZIO: Skeleton code for a ZIO 2 application that runs forever, on a schedule

One of My “Top-5” Meditation Books
Practicing
the
Jhanas

As a brief note today, here’s some skeleton code for a ZIO (ZIO 2) application that needs to run forever. It (a) does what it needs to do, then (b) sleeps for a minute, and then (c) goes back to Step A, doing whatever it needs to do, forever.

I wanted to share this code because I think it’s probably a very common pattern and need in the ZIO world — writing an application that needs to run forever, on a schedule.

Also note that the code is written using Scala 3, but the syntax can be changed to use Scala 2, if you need to. I also added a layer/ZLayer to the code to make it a little more “real world.”

All that being said, here’s the ZIO “run forever” code:

import zio.*
import java.nio.file.Path

object FileMovingApp extends ZIOAppDefault:

    // Define the database service interface
    trait Database:
        def checkForFilesToMove(): Task[Option[Path]]

    // Service to move files
    object FileService:
        def moveFile(file: Path): Task[Unit] =
            ZIO.attempt {
                // Simulate file move
                println(s"Moving file: $file")
            }

    // The main processing logic
    def processFile(): ZIO[Database & Console, Throwable, Unit] =
        for
            maybeFile <- ZIO.serviceWithZIO[Database](_.checkForFilesToMove())
            _         <- maybeFile match
                            case Some(file) =>
                                for
                                    _ <- Console.printLine(s"Found file to move: $file")
                                    _ <- FileService.moveFile(file)
                                yield ()
                            case None =>
                                Console.printLine("No files to move")
        yield ()

    // Live implementation of the Database service (placeholder)
    val dbLayer: ULayer[Database] =
        ZLayer.succeed(
            new Database:
                def checkForFilesToMove(): Task[Option[Path]] =
                    ZIO.succeed(None) // Stub logic
        )

    // The main "run forever" ZIO application, with scheduled execution
    def run =
        processFile()
            .repeat(Schedule.spaced(1.minute))
            .provide(
                dbLayer,
                Console.live
            )

Please note that because this is just some skeleton code, I haven’t checked it carefully for errors, but I think it’s at least in the ballpark of being accurate.