Skip to content

Instantly share code, notes, and snippets.

@mmenestret
Last active February 26, 2019 14:10
Show Gist options
  • Save mmenestret/6ad7d8395ec070f03d623d4522e48a0c to your computer and use it in GitHub Desktop.
Save mmenestret/6ad7d8395ec070f03d623d4522e48a0c to your computer and use it in GitHub Desktop.
Tagless final -> Explicit dependencies passing -> ReaderT
object BoilerPlate {
// Some random effect type classes we'll want to stack (MTL)
trait IOEffect[F[_]]
trait StateEffect[F[_]]
// Our "final" type that match our needs, providing the typeclass instances needed by our algebras implementations
// It's often a monad transformer stack or custom type classes implementations
type MyF[A]
implicit def myFIO: IOEffect[MyF] = ???
implicit def myFState: StateEffect[MyF] = ???
// Our business domain algebras
trait Alg1[F[_]] {
def ope1[A]: F[A]
}
trait Alg2[F[_]] {
def ope2[A]: F[A]
}
}
object TaglessFinal {
import BoilerPlate._
// Our MTL algebras interpreters
implicit def implAlg1[F[_]: IOEffect]: Alg1[F] = ???
implicit def implAlg2[F[_]: StateEffect]: Alg2[F] = ???
// Our program, using type classes encoding to provide our interpreters
def program[F[_]: Alg1[F]: Alg2[F]](): F[Unit] = ???
program[MyF]() // Go
}
object ExplicitEncoding {
import BoilerPlate._
// Our algebras interpreters
final case class ImplAlg1[F[_]: IOEffect]() extends Alg1[F] { def ope1[A]: F[A] = ??? }
final case class ImplAlg2[F[_]: StateEffect]() extends Alg2[F] { def ope2[A]: F[A] = ??? }
// Our program that takes it's dependencies explicitly (and does not rely on type class encoding)
def program[F[_]](alg1: Alg1[F], alg2: Alg2[F]): F[Unit] = ???
program[MyF](ImplAlg1(), ImplAlg2()) // Go
}
object ReaderTEncoding {
import BoilerPlate._
final case class ImplAlg1[F[_]: IOEffect]() extends Alg1[F] { def ope1[A]: F[A] = ??? }
final case class ImplAlg2[F[_]: StateEffect]() extends Alg2[F] { def ope2[A]: F[A] = ??? }
final case class Env[F[_]](
alg1: ImplAlg1[F],
alg2: ImplAlg2[F]
)
// Every time needed, providing dependencies explicitly is cumbersome.
// Let's pack them "all together" in a `Env` record of functions
// and define ReaderT[Env, F, ?] functions so we can provide `Env` later
def program[F[_]]: ReaderT[F, Env, Unit] = ???
val myProdEnv: Env[MyF] = Env(ImplAlg1[MyF](), ImplAlg2[MyF]())
program[MyF].run(myProdEnv) // Go
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment