Skip to content

Instantly share code, notes, and snippets.

@Jacke
Forked from jacemale/mtl.scala
Created June 18, 2020 22:46
Show Gist options
  • Select an option

  • Save Jacke/29df8829ec757fa6c928084ccbe6f5d1 to your computer and use it in GitHub Desktop.

Select an option

Save Jacke/29df8829ec757fa6c928084ccbe6f5d1 to your computer and use it in GitHub Desktop.
import cats.data.{EitherT, State}
import cats.effect.Sync
import cats.implicits._
import cats.mtl.MonadState
import cats.mtl.implicits._
import cats.tagless.implicits._
import cats.tagless.{Derive, FunctorK}
import cats.{Bifunctor, MonadError, ~>}
import com.itv.heimdall.models.{DbError, ErrorType}
trait MyDataStore[F[_]] {
def put(k: String, a: String): F[Unit]
def get(k: String): F[Option[String]]
}
object MyDataStore {
def apply[F[_]: MyDataStore]: MyDataStore[F] = implicitly[MyDataStore[F]]
// test instance
implicit def mockInstance[F[_]](implicit S: MonadState[F, Map[String, String]]): MyDataStore[F] =
new MyDataStore[F] {
override def put(k: String, a: String): F[Unit] = S.modify(_.updated(k, a))
override def get(k: String): F[Option[String]] = S.inspect(_.get(k))
}
// derive a safe ZIO instance from a cats-effect instance with the help of cats-tagless (you may not need this)
implicit def functorKForMyDataStore: FunctorK[MyDataStore] =
Derive.functorK[MyDataStore]
// instance for ZIO cats-effect interop
implicit def mySafeDataStore[F[_, _]](implicit myDataStore: MyDataStore[F[Throwable, *]],
B: Bifunctor[F],
F: Sync[F[Throwable, *]]): MyDataStore[F[ErrorType, *]] =
myDataStore.mapK(λ[F[Throwable, *] ~> F[ErrorType, *]](fa => fa.leftMap(ex => DbError(ex.getMessage))))
}
object CatsEffectDataStore {
// some cats-effect based impl (imagine doobie)
def instance[F[_]: Sync](): MyDataStore[F] =
new MyDataStore[F] {
override def put(k: String, a: String): F[Unit] = Sync[F].raiseError(new Exception(""))
override def get(k: String): F[Option[String]] = Sync[F].raiseError(new Exception(""))
}
}
// my business logic
def myNiceZioCode[F[_]: MonadError[*[_], ErrorType]: MyDataStore]: F[Unit] =
for {
_ <- MyDataStore[F].put("asdasd", "asd")
_ <- DbError("asd").raiseError[F, Unit]
} yield ()
// my tests
type TestMonad[A] = EitherT[State[Map[String, String], *], ErrorType, A]
val (state, error) = myNiceZioCode[TestMonad].value.runEmpty.value
state shouldBe Map("asdasd", "asd")
error shouldBe Left(DbError("asd"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment