Skip to content

Instantly share code, notes, and snippets.

@programaker
Last active May 31, 2021 08:38
Show Gist options
  • Save programaker/51c31e8c1ca3d4715b872d610c1a636c to your computer and use it in GitHub Desktop.
Save programaker/51c31e8c1ca3d4715b872d610c1a636c to your computer and use it in GitHub Desktop.

Revisions

  1. programaker revised this gist May 31, 2021. 1 changed file with 30 additions and 21 deletions.
    51 changes: 30 additions & 21 deletions context-functions.scala
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,16 @@
    import cats.Monad
    import cats.syntax.functor.*
    import cats.effect.IO
    import cats.effect.unsafe.implicits.global

    case class Frunfles(id: Long, name: String)

    case class DatabaseConnection()
    trait DatabaseConnection:
    def exec[F[_]: Monad](op: String): F[Unit]

    object DatabaseConnection:
    given DatabaseConnection = DatabaseConnection()
    given DatabaseConnection with
    def exec[F[_]: Monad](op: String): F[Unit] = Monad[F].pure(println(s">>> DB op = $op"))

    // single-dependency "primitive" environments
    type MonadEnv[F[_], A] = Monad[F] ?=> F[A]
    @@ -22,30 +26,35 @@ type Env2[F[_], A] = DatabaseEnv[MonadEnv[F, A]]
    type Env3[F[_], A] = (DatabaseConnection, Monad[F]) ?=> F[A]
    type Env4[F[_], A] = (Monad[F], DatabaseConnection) ?=> F[A]

    // need Env1
    def findAllFrunfles[F[_]]: Env[F, List[Frunfles]] = Monad[F].pure(List.empty)
    def findFrunflesById[F[_]](id: Long): Env[F, Option[Frunfles]] = Monad[F].pure(None)
    // need "Env"
    def findAllFrunfles[F[_]]: Env[F, List[Frunfles]] =
    summon[DatabaseConnection].exec("findAll").map(_ => List.empty)
    def findFrunflesById[F[_]](id: Long): Env[F, Option[Frunfles]] =
    summon[DatabaseConnection].exec("findById").map(_ => None)

    // need Env2
    def findFrunflesByValue[F[_]](value: String): Env2[F, Option[Frunfles]] = Monad[F].pure(None)
    def insertFrunfles[F[_]](f: Frunfles): Env2[F, Unit] = Monad[F].pure(())
    // need "Env2"
    def insertFrunfles[F[_]](f: Frunfles): Env2[F, Unit] =
    summon[DatabaseConnection].exec("insert")

    // need Env3
    def updateFrunfles[F[_]](f: Frunfles): Env3[F, Unit] = Monad[F].pure(())
    // need "Env3"
    def updateFrunfles[F[_]](f: Frunfles): Env3[F, Unit] =
    summon[DatabaseConnection].exec("update")

    // need Env4
    def deleteFrunfles[F[_]](id: Long): Env4[F, Unit] = Monad[F].pure(())
    // need "Env4"
    def deleteFrunfles[F[_]](id: Long): Env4[F, Unit] =
    summon[DatabaseConnection].exec("delete")

    // nesting order does not matter!
    // parameter order does not matter!
    // mixing different envs does not matter!
    // the functions are called as if the envs don't exist at all!
    val res: IO[Unit] =
    for
    _ <- insertFrunfles[IO](Frunfles(1L, "frunfles"))
    f <- findFrunflesById[IO](1L)
    _ <- updateFrunfles[IO](Frunfles(1L, "sbrubbles"))
    _ <- deleteFrunfles[IO](1L)
    yield ()

    val fs = findAllFrunfles[IO].unsafeRunSync()
    val io: IO[Unit] =
    for
    _ <- insertFrunfles[IO](Frunfles(1L, "frunfles"))
    _ <- findFrunflesById[IO](1L)
    _ <- updateFrunfles[IO](Frunfles(1L, "sbrubbles"))
    _ <- deleteFrunfles[IO](1L)
    yield ()

    val res1 = io.unsafeRunSync()
    val res2 = findAllFrunfles[IO].unsafeRunSync()
  2. programaker revised this gist May 28, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions context-functions.scala
    Original file line number Diff line number Diff line change
    @@ -39,6 +39,7 @@ def deleteFrunfles[F[_]](id: Long): Env4[F, Unit] = Monad[F].pure(())
    // nesting order does not matter!
    // parameter order does not matter!
    // mixing different envs does not matter!
    // the functions are called as if the envs don't exist at all!
    val res: IO[Unit] =
    for
    _ <- insertFrunfles[IO](Frunfles(1L, "frunfles"))
  3. programaker revised this gist May 28, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion context-functions.scala
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ type MonadEnv[F[_], A] = Monad[F] ?=> F[A]
    type DatabaseEnv[A] = DatabaseConnection ?=> A

    // composing envs by nesting
    // bothe are equivalent!
    // both are equivalent!
    type Env[F[_], A] = MonadEnv[F, DatabaseEnv[A]]
    type Env2[F[_], A] = DatabaseEnv[MonadEnv[F, A]]

  4. programaker revised this gist May 28, 2021. 1 changed file with 19 additions and 4 deletions.
    23 changes: 19 additions & 4 deletions context-functions.scala
    Original file line number Diff line number Diff line change
    @@ -8,22 +8,37 @@ case class DatabaseConnection()
    object DatabaseConnection:
    given DatabaseConnection = DatabaseConnection()

    type MonadR[F[_], A] = Monad[F] ?=> F[A]
    type DatabaseR[A] = DatabaseConnection ?=> A
    type Env[F[_], A] = MonadR[F, DatabaseR[A]]
    type Env2[F[_], A] = DatabaseR[MonadR[F, A]]
    // single-dependency "primitive" environments
    type MonadEnv[F[_], A] = Monad[F] ?=> F[A]
    type DatabaseEnv[A] = DatabaseConnection ?=> A

    // composing envs by nesting
    // bothe are equivalent!
    type Env[F[_], A] = MonadEnv[F, DatabaseEnv[A]]
    type Env2[F[_], A] = DatabaseEnv[MonadEnv[F, A]]

    // multi-dependency environments
    // both are equivalent!
    type Env3[F[_], A] = (DatabaseConnection, Monad[F]) ?=> F[A]
    type Env4[F[_], A] = (Monad[F], DatabaseConnection) ?=> F[A]

    // need Env1
    def findAllFrunfles[F[_]]: Env[F, List[Frunfles]] = Monad[F].pure(List.empty)
    def findFrunflesById[F[_]](id: Long): Env[F, Option[Frunfles]] = Monad[F].pure(None)

    // need Env2
    def findFrunflesByValue[F[_]](value: String): Env2[F, Option[Frunfles]] = Monad[F].pure(None)
    def insertFrunfles[F[_]](f: Frunfles): Env2[F, Unit] = Monad[F].pure(())

    // need Env3
    def updateFrunfles[F[_]](f: Frunfles): Env3[F, Unit] = Monad[F].pure(())

    // need Env4
    def deleteFrunfles[F[_]](id: Long): Env4[F, Unit] = Monad[F].pure(())

    // nesting order does not matter!
    // parameter order does not matter!
    // mixing different envs does not matter!
    val res: IO[Unit] =
    for
    _ <- insertFrunfles[IO](Frunfles(1L, "frunfles"))
  5. programaker created this gist May 28, 2021.
    35 changes: 35 additions & 0 deletions context-functions.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    import cats.Monad
    import cats.effect.IO
    import cats.effect.unsafe.implicits.global

    case class Frunfles(id: Long, name: String)

    case class DatabaseConnection()
    object DatabaseConnection:
    given DatabaseConnection = DatabaseConnection()

    type MonadR[F[_], A] = Monad[F] ?=> F[A]
    type DatabaseR[A] = DatabaseConnection ?=> A
    type Env[F[_], A] = MonadR[F, DatabaseR[A]]
    type Env2[F[_], A] = DatabaseR[MonadR[F, A]]
    type Env3[F[_], A] = (DatabaseConnection, Monad[F]) ?=> F[A]
    type Env4[F[_], A] = (Monad[F], DatabaseConnection) ?=> F[A]

    def findAllFrunfles[F[_]]: Env[F, List[Frunfles]] = Monad[F].pure(List.empty)
    def findFrunflesById[F[_]](id: Long): Env[F, Option[Frunfles]] = Monad[F].pure(None)

    def findFrunflesByValue[F[_]](value: String): Env2[F, Option[Frunfles]] = Monad[F].pure(None)
    def insertFrunfles[F[_]](f: Frunfles): Env2[F, Unit] = Monad[F].pure(())

    def updateFrunfles[F[_]](f: Frunfles): Env3[F, Unit] = Monad[F].pure(())
    def deleteFrunfles[F[_]](id: Long): Env4[F, Unit] = Monad[F].pure(())

    val res: IO[Unit] =
    for
    _ <- insertFrunfles[IO](Frunfles(1L, "frunfles"))
    f <- findFrunflesById[IO](1L)
    _ <- updateFrunfles[IO](Frunfles(1L, "sbrubbles"))
    _ <- deleteFrunfles[IO](1L)
    yield ()

    val fs = findAllFrunfles[IO].unsafeRunSync()