Skip to content

Instantly share code, notes, and snippets.

@Jacoby6000
Last active May 1, 2018 01:00
Show Gist options
  • Save Jacoby6000/bc0d001b946c1cb7bfbf4bc65d4c577f to your computer and use it in GitHub Desktop.
Save Jacoby6000/bc0d001b946c1cb7bfbf4bc65d4c577f to your computer and use it in GitHub Desktop.
LoggerT
import cats.implicits._
import cats.{FlatMap, Monad, Applicative, Apply, Functor}
/**
* A concretion/simplification of StateT for logging.
*/
class LoggerT[F[_], Ctx, A](val run: F[(Ctx, A)]) {
def value(implicit: F: Functor[F]): F[A] = run.map(_._2)
def context(implicit: F: Functor[F]): F[A] = run.map(_._1)
def appendContext(newCtx: Ctx)(implicit F: Applicative[F], semigroup: Semigroup[Ctx]): LoggerT[F, Ctx, A] =
mapContext(_ |+| newCtx)
def appendContextF(newCtx: F[Ctx])(implicit F: Apply[F], semigroup: Semigroup[Ctx]): LoggerT[F, Ctx, A] =
new LoggerT(F.map2(context, newCtx)(_ |+| _), value)
def setContext[B](newCtx: Ctx)(implicit F: Applicative[F]): LoggerT[F, B, A] =
new LoggerT(newCtx.pure[F], value)
def setContextF[B](newCtx: F[Ctx]): LoggerT[F, B, A] =
new LoggerT(newCtx, value)
def mapContext[B](f: Ctx => B)(implicit F: Functor[F]): LoggerF[F, B, A] =
new LoggerT(context.map(f), value)
def map[B](f: A => B)(implicit F: Functor[F]): LoggerT[F, Ctx, B] =
new LoggerT(context, value.map(f))
def flatMap[B](f: A => LoggerT[F, Ctx, B])(implicit F: FlatMap[F], semigroup: Semigroup[Ctx]): LoggerT[F, Ctx, B] = {
val newContextAndValue = value.flatMap(f(_).tupled)
new LoggerT(newContextAndValue.map(_._1), newContextAndValue.map(_._2))
}
def flatMapF[B](f: A => F[B])(implicit F: FlatMap[F]): LoggerT[F, Ctx, B] =
new LoggerT(context, value.flatMap(f))
def flatMapContext(f: Ctx => F[B])(implicit F: FlatMap[F]): LoggerT[F, B, Ctx] =
new LoggerT(context.flatMap(f), value)
// LogSystem.log is some slf4j logger.
def log[X: IWrites: Monoid](x: X, level: LogLevel)(implicit ctxWrites: IWrites[Ctx], F: Effect[F]): LoggerT[F, Ctx, A] =
new LoggerT(context, fa << context.flatTap { ctx => F.delay {
LogSystem.log(
JsObject(
Seq("_context" -> ctx, x),
level
)
)
}})
// LogSystem.log is some slf4j logger.
def logF[X: IWrites: Monoid](fx: F[X], level: LogLevel)(implicit ctxWrites: IWrites[Ctx], F: Effect[F]): LoggerT[F, Ctx, A] =
new LoggerT(context, fa << (fx, context).mapN { ctx =>
LogSystem.log(
JsObject(
Seq("_context" -> ctx, fx),
level
)
)
})
def error(a: Shows): LoggerT[F, Ctx, A] = log(a, Error)
def info(a: Shows): LoggerT[F, Ctx, A] = log(a, Info)
def debug(a: Shows): LoggerT[F, Ctx, A] = log(a, Debug)
def verbose(a: Shows): LoggerT[F, Ctx, A] = log(a, Verbose)
def warning(a: Shows): LoggerT[F, Ctx, A] = log(a, Warning)
}
object LoggerT {
implicit def loggerTFunctor[F[_]: Functor, A]: Functor[LoggerT[F, A, ?]] =
new Functor[LoggerT[F, A, ?]] {
def map[B, C](fb: LoggerT[F, A, B])(f: B => C): LoggerT[F, A, C] = fb.map(f)
}
implicit def loggerTApply[F[_]: Apply, A: Semigroup]: Functor[LoggerT[F, A, ?]] =
new Apply[LoggerT[F, A, ?]] {
def ap[B, C](ff: LoggerT[F, A, B => C])(fb: LoggerT[F, A, B]): LoggerT[F, Ctx, B] =
new LoggerT(F.map2(ff.context, fb.context)(_ |+| _), ff.value <*> fb.value)
}
implicit def loggerTApplicative[F[_]: Applicative, A: Semigroup]: Functor[LoggerT[F, A, ?]] =
new Apply[LoggerT[F, A, ?]] {
def ap[B, C](ff: LoggerT[F, A, B => C])(fb: LoggerT[F, A, B]): LoggerT[F, Ctx, B] =
new LoggerT(F.map2(ff.context, fb.context)(_ |+| _), ff.value <*> fb.value)
}
}
sealed abstract class LogLevel(val value: Int, val name: String) extends IntEnumEntry
object LogLevel {
implicit val logLevelOrder = Order.fromScalaOrdering[Int].contramap[LogLevel](_.value)
implicit val logLevelShow = Show.show[LogLevel](_.name)
case object Debug extends LogLevel(0, "DEBUG")
case object Verbose extends LogLevel(1, "VERBOSE")
case object Info extends LogLevel(2, "INFO")
case object Warning extends LogLevel(3, "WARNING")
case object Error extends LogLevel(4, "ERROR")
val values = findValues
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment