The cake is a lie
trait Log[F[_]] { self =>
def info(out: String): F[Unit]
def warn(out: String): F[Unit]
}
final class NoisyLog[F[_]](log: Log[F[_]]) {
def info(out: String): F[Unit] = log.warn(out)
def warn(out: String): F[Unit] = log.warn(out)
}
object SilentLog extends Log[Task] {
def info(out: String): Task[Unit] = Task.eval(())
def warn(out: String): Task[Unit] = Task.eval(())
}
object JavaLog extends Log[Task] {
def info(out: String): Task[Unit] = Task.eval(System.out.println(out))
def warn(out: String): Task[Unit] = Task.eval(System.err.println(out))
}
object Main {
def mkLog(silent: Boolean, noisy: Boolean): Log[Task] =
if (silent) SilentLog
else if (noisy) NoisyLog(JavaLog)
else JavaLog
implicit val log = mkConfig(silent = false, noisy = false)
}
// finally tagless action constrained with Constraint
// that uses the service Service
trait Action[Service[_[_]], Constraint[_[_]], A] {
def apply[G[_] : Constraint: Service]: G[A]
}
trait Cartesian[F[_]] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}
def and[F[_], G[_], H[_]]
(first: F ~> G,
second: F ~> H): F ~> Prod[G, H, ?] =
Lambda[F ~> Prod[G, H, ?]].apply(fa => Prod(first(fa), second(fa)))