Last active
March 23, 2023 15:28
-
-
Save tvoklov/187defe698fd8863d6c8699ea1ad34fe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cats.effect.{Async, Clock, IO, IOApp, Ref} | |
import cats.syntax.all._ | |
import java.time.LocalDateTime | |
trait Metric[F[_]] { | |
def modify(name: String, by: Int): F[Unit] | |
def set(name: String, value: Int): F[Unit] | |
} | |
object Metric { | |
def apply[F[_]: Metric]: Metric[F] = implicitly | |
} | |
trait Log[F[_]] { | |
def log(value: String): F[Unit] | |
} | |
object Log { | |
def apply[F[_]: Log]: Log[F] = implicitly | |
} | |
object Play extends IOApp.Simple { | |
def functionUsingMetric[F[_]: Async: Metric](param: Int) = | |
for { | |
_ <- Async[F].delay(println(s"setting metric to $param")) | |
_ <- Metric[F].set("metric 1", param) | |
_ <- Async[F].delay(println(s"increasing metric by $param")) | |
_ <- Metric[F].modify("metric 1", param) | |
} yield () | |
def functionUsingLogger[F[_]: Async: Log](param: Int) = | |
for { | |
_ <- Async[F].delay(println(s"logging $param")) | |
_ <- Log[F].log(s"this is the param = $param") | |
} yield () | |
def functionUsingBothTypeclasses[F[_]: Async: Metric: Log](param: Int) = | |
for { | |
_ <- functionUsingMetric(param) | |
_ <- functionUsingLogger(param) | |
} yield () | |
implicit val runtimeForLog: Log[IO] = new Log[IO] { | |
override def log(value: String): IO[Unit] = for { | |
a <- IO.delay(LocalDateTime.now()) | |
_ <- IO.println(s"$a : $value") | |
} yield () | |
} | |
// prints out the metric modifications | |
def defaultRuntimeForMetric[F[_]: Async] = new Metric[F] { | |
override def modify(name: String, by: Int): F[Unit] = | |
Async[F].delay(println(s"modifying $name by $by")) | |
override def set(name: String, value: Int): F[Unit] = | |
Async[F].delay(println(s"setting $name to $value")) | |
} | |
def metricUsingIORef[F[_]: Async](ref: Ref[F, Map[String, Int]]) = | |
new Metric[F] { | |
override def modify(name: String, by: Int): F[Unit] = | |
ref.update(_.updatedWith(name) { | |
case Some(value) => Some(value + by) | |
case None => Some(by) | |
}) | |
override def set(name: String, value: Int): F[Unit] = | |
ref.update(_.updated(name, value)) | |
} | |
// | |
// pseudo-code, shows how to provide a runtime that takes parameters | |
// | |
// def metricSendingSomethingSomewhere[F[_]: Async](server: Client[F]) = new Metric[F] { | |
// | |
// override def modify(name: String, by: Int): F[Unit] = { | |
// val request: String = Request( | |
// method = POST, | |
// path = s"/$name/update?by=$by", | |
// ) | |
// | |
// Client.send(request) | |
// } | |
// | |
// override def set(name: String, value: Int): F[Unit] = { | |
// val request: String = Request( | |
// method = POST, | |
// path = s"/$name/set?value=$value" | |
// ) | |
// | |
// Client.send(request) | |
// } | |
// } | |
override def run: IO[Unit] = | |
for { | |
_ <- functionUsingLogger[IO](12) | |
_ <- { | |
// these implicit vals are going to be at the top | |
// everywhere else it's just going to use an F[_] | |
implicit val drfm: Metric[IO] = defaultRuntimeForMetric[IO] | |
functionUsingMetric[IO](15) | |
} | |
ref <- Ref[IO].of(Map.empty[String, Int]) | |
_ <- { | |
implicit val rfm: Metric[IO] = metricUsingIORef(ref) | |
functionUsingBothTypeclasses[IO](20) | |
} | |
refAfter <- ref.get | |
_ <- IO.println(s"metric after updates: $refAfter") | |
} yield () | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment