import cats.effect.IO
import freestyle.free._
import freestyle.free.implicits._

@free trait Logger {
  def debug(message: String): FS[Unit]
}

@free trait Summer {
  def sum(a: Int, b: Int): FS[Int]
}

@module trait FreeApp {
  val log: Logger
  val summer: Summer

  def program[F[_]](a: Int, b: Int)(implicit app: FreeApp[F]): FreeS[F, Int] =
    for {
      sum <- app.summer.sum(a, b)
      _   <- app.log.debug(s"$a + $b = $sum")
    } yield sum
}

object FreeModulesExample extends App {

  implicit val loggerHandler = new Logger.Handler[IO] {
    def debug(message: String): IO[Unit] = IO { println(s"Logger debug: $message") }
  }

  implicit val summerHandler = new Summer.Handler[IO] {
    override def sum(a: Int, b: Int): IO[Int] = IO { a + b }
  }

  val result = FreeApp.instance.program[FreeApp.Op](5, 3).interpret[IO].unsafeRunSync()
  println(s"Result is: $result")

  // Logger debug: 5 + 3 = 8
  // Result is: 8
}