Skip to content

Commit

Permalink
Fix #94, add catz.core object with implicits that require only cats-c…
Browse files Browse the repository at this point in the history
…ore, not cats-effect (#95)

* Fix #94, add catz.core object with implicits that require only cats-core, not cats-effect

- also update README.md

* include core-only-test in tests on CI
  • Loading branch information
neko-kai authored Oct 20, 2019
1 parent 5ccbf3d commit 231e005
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 28 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

This library provides instances required by Cats Effect.

## `IO` Cats Effect's instances
## `ZIO` Cats Effect instances

**ZIO** integrates with Typelevel libraries by providing an instance of `ConcurrentEffect` for `IO` as required, for instance, by `fs2`, `doobie` and `http4s`. Actually, I lied a little bit, it is not possible to implement `ConcurrentEffect` for any error type since `ConcurrentEffect` extends `MonadError` of `Throwable`.

Expand All @@ -17,6 +17,22 @@ For convenience we have defined an alias as follow:

Therefore, we provide an instance of `ConcurrentEffect[Task]`.

## ConcurrentEffect

In order to get a `ConcurrentEffect[Task]` or `ConcurrentEffect[RIO[R, *]]` we need an implicit `Runtime[R]` in scope. The easiest way to get it is using `ZIO.runtime`:

```scala
import cats.effect._
import zio._
import zio.interop.catz._

def getCE = {
ZIO.runtime.map { implicit r: Runtime[Any] =>
val F: ConcurrentEffect[Task] = implicitly
}
}
```

### Timer

In order to get a `cats.effect.Timer[Task]` instance we need an extra import:
Expand All @@ -27,6 +43,16 @@ import zio.interop.catz.implicits._

The reason it is not provided by the default "interop" import is that it makes testing programs that require timing capabilities hard so an extra import wherever needed makes reasoning about it much easier.

### cats-core

If you only need instances for `cats-core` typeclasses, not `cats-effect` import `zio.interop.catz.core._`:

````scala
import zio.interop.catz.core._
````

Note that this library only has an `Optional` dependency on cats-effect – if you or your libraries don't depend on it, this library will not add it to the classpath.

### Example

The following example shows how to use ZIO with Doobie (a library for JDBC access) and FS2 (a streaming library), which both rely on Cats Effect instances:
Expand Down
27 changes: 23 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sbtcrossproject.CrossPlugin.autoImport.crossProject
import explicitdeps.ExplicitDepsPlugin.autoImport.moduleFilterRemoveValue
import BuildHelper._
import explicitdeps.ExplicitDepsPlugin.autoImport.moduleFilterRemoveValue
import sbtcrossproject.CrossPlugin.autoImport.crossProject

name := "interop-cats"

Expand Down Expand Up @@ -30,8 +30,8 @@ ThisBuild / publishTo := sonatypePublishToBundle.value

addCommandAlias("fmt", "all scalafmtSbt scalafmt test:scalafmt")
addCommandAlias("check", "all scalafmtSbtCheck scalafmtCheck test:scalafmtCheck")
addCommandAlias("testJVM", ";interopCatsJVM/test")
addCommandAlias("testJS", ";interopCatsJS/test")
addCommandAlias("testJVM", ";interopCatsJVM/test;coreOnlyTestJVM/test")
addCommandAlias("testJS", ";interopCatsJS/test;coreOnlyTestJS/test")

lazy val root = project
.in(file("."))
Expand Down Expand Up @@ -72,3 +72,22 @@ lazy val interopCatsJS = interopCats.js
.settings(
libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % "2.0.0-RC3" % Test
)

lazy val coreOnlyTest = crossProject(JSPlatform, JVMPlatform)
.in(file("core-only-test"))
.dependsOn(interopCats)
.settings(stdSettings("core-only-test"))
.settings(skip in publish := true)
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-core" % "2.0.0" % Test,
"dev.zio" %%% "zio-test-sbt" % "1.0.0-RC15" % Test
)
)
.settings(testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"))

lazy val coreOnlyTestJVM = coreOnlyTest.jvm
lazy val coreOnlyTestJS = coreOnlyTest.js
.settings(
libraryDependencies += "io.github.cquiroz" %%% "scala-java-time" % "2.0.0-RC3" % Test
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package zio.interop.test

import cats.data.NonEmptyList
import cats.{ Bifunctor, Monad, MonadError, SemigroupK }
import zio._
import zio.interop.catz.core._
import zio.stream.interop.catz.core._
import zio.stream.{ Stream, ZStream }
import zio.test.Assertion._
import zio.test.{ DefaultRunnableSpec, test, _ }

object CoreSummonSpec
extends DefaultRunnableSpec(
suite("summons from catz.core work with only a cats-core dependency")(
test("ZIO instances") {
val monad = implicitly[Monad[UIO]]
val monadError = implicitly[MonadError[Task, Throwable]]
val semigroupK = implicitly[SemigroupK[IO[NonEmptyList[Unit], ?]]]
val bifunctor = implicitly[Bifunctor[IO]]

monad.map(ZIO.unit)(identity)
monadError.map(ZIO.unit)(identity)
semigroupK.combineK(ZIO.unit, ZIO.unit)
bifunctor.leftMap(ZIO.fromOption(None))(identity)

assert((), anything)
},
test("ZManaged instances") {
val monad = implicitly[Monad[ZManaged[Any, Nothing, ?]]]
val monadError = implicitly[MonadError[Managed[Throwable, ?], Throwable]]
val semigroupK = implicitly[SemigroupK[Managed[Nothing, ?]]]
val bifunctor = implicitly[Bifunctor[Managed]]

monad.map(ZManaged.unit)(identity)
monadError.map(ZManaged.unit)(identity)
semigroupK.combineK(ZManaged.unit, ZManaged.unit)
bifunctor.leftMap(ZManaged.fail(()))(identity)

assert((), anything)
},
test("ZStream instances") {
val monad = implicitly[Monad[ZStream[Any, Nothing, ?]]]
val monadError = implicitly[MonadError[Stream[Throwable, ?], Throwable]]
val semigroupK = implicitly[SemigroupK[Stream[Nothing, ?]]]
val bifunctor = implicitly[Bifunctor[Stream]]

monad.map(ZStream.unit)(identity)
monadError.map(ZStream.unit)(identity)
semigroupK.combineK(ZStream.unit, ZStream.unit)
bifunctor.leftMap(ZStream.fail(()))(identity)

assert((), anything)
}
)
)
26 changes: 20 additions & 6 deletions interop-cats/shared/src/main/scala/zio/interop/cats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@ import zio.clock.Clock
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.{ FiniteDuration, NANOSECONDS, TimeUnit }

object catz extends CatsPlatform {
object catz extends CatsEffectPlatform {
object core extends CatsPlatform
object mtl extends CatsMtlPlatform
object test extends CatsTestFunctions
}

abstract class CatsPlatform extends CatsInstances with CatsZManagedInstances with CatsZManagedSyntax {
abstract class CatsEffectPlatform
extends CatsEffectInstances
with CatsEffectZManagedInstances
with CatsZManagedInstances
with CatsZManagedSyntax {
val console: interop.console.cats.type = interop.console.cats

trait CatsApp extends App {
Expand All @@ -55,7 +60,10 @@ abstract class CatsPlatform extends CatsInstances with CatsZManagedInstances wit
}
}

abstract class CatsInstances extends CatsInstances1 {
abstract class CatsPlatform extends CatsInstances with CatsZManagedInstances

abstract class CatsEffectInstances extends CatsInstances with CatsEffectInstances1 {

implicit def zioContextShift[R, E]: ContextShift[ZIO[R, E, *]] = new ContextShift[ZIO[R, E, *]] {
override def shift: ZIO[R, E, Unit] =
ZIO.yieldNow
Expand All @@ -80,6 +88,15 @@ abstract class CatsInstances extends CatsInstances1 {
implicit def taskEffectInstance[R](implicit runtime: Runtime[R]): effect.ConcurrentEffect[RIO[R, *]] =
new CatsConcurrentEffect[R](runtime)

}

sealed trait CatsEffectInstances1 {
implicit def taskConcurrentInstance[R]: effect.Concurrent[RIO[R, *]] =
new CatsConcurrent[R]
}

abstract class CatsInstances extends CatsInstances1 {

implicit def monoidKInstance[R, E: Monoid]: MonoidK[ZIO[R, E, *]] =
new CatsMonoidK[R, E]

Expand All @@ -91,9 +108,6 @@ abstract class CatsInstances extends CatsInstances1 {

sealed abstract class CatsInstances1 extends CatsInstances2 {

implicit def taskConcurrentInstance[R]: effect.Concurrent[RIO[R, *]] =
new CatsConcurrent[R]

implicit def parallelInstance[R, E]: Parallel.Aux[ZIO[R, E, *], ParIO[R, E, *]] =
new CatsParallel[R, E](monadErrorInstance)

Expand Down
20 changes: 12 additions & 8 deletions interop-cats/shared/src/main/scala/zio/interop/catszmanaged.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ final class ZManagedSyntax[R, E, A](private val managed: ZManaged[R, E, A]) exte

}

trait CatsEffectZManagedInstances {

implicit def liftIOZManagedInstances[R](
implicit ev: LiftIO[ZIO[R, Throwable, ?]]
): LiftIO[ZManaged[R, Throwable, ?]] =
new LiftIO[ZManaged[R, Throwable, ?]] {
override def liftIO[A](ioa: CIO[A]): ZManaged[R, Throwable, A] =
ZManaged.fromEffect(ev.liftIO(ioa))
}

}

trait CatsZManagedInstances extends CatsZManagedInstances1 {

implicit def monadErrorZManagedInstances[R, E]: MonadError[ZManaged[R, E, ?], E] =
Expand All @@ -92,14 +104,6 @@ trait CatsZManagedInstances extends CatsZManagedInstances1 {
override def combine(x: ZManaged[R, E, A], y: ZManaged[R, E, A]): ZManaged[R, E, A] = x.zipWith(y)(ev.combine)
}

implicit def liftIOZManagedInstances[R, A](
implicit ev: LiftIO[ZIO[R, Throwable, ?]]
): LiftIO[ZManaged[R, Throwable, ?]] =
new LiftIO[ZManaged[R, Throwable, ?]] {
override def liftIO[A](ioa: CIO[A]): ZManaged[R, Throwable, A] =
ZManaged.fromEffect(ev.liftIO(ioa))
}

}

sealed trait CatsZManagedInstances1 {
Expand Down
20 changes: 11 additions & 9 deletions interop-cats/shared/src/main/scala/zio/stream/interop/cats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,35 @@ import cats.arrow._
import zio._
import zio.stream._

object catz extends CatsInstances
object catz extends CatsInstances {
object core extends CatsInstances
}

sealed abstract class CatsInstances extends CatsInstances1 {
implicit def alternativeInstance[R, E]: Alternative[ZStream[R, E, *]] =
implicit def zstreamAlternativeInstance[R, E]: Alternative[ZStream[R, E, *]] =
new CatsAlternative[R, E]
}

sealed abstract class CatsInstances1 extends CatsInstances2 {
implicit def monoidKInstance[R, E]: MonoidK[ZStream[R, E, *]] =
implicit def zstreamMonoidKInstance[R, E]: MonoidK[ZStream[R, E, *]] =
new CatsMonoidK[R, E]

implicit def bifunctorInstance[R]: Bifunctor[ZStream[R, *, *]] =
implicit def zstreamBifunctorInstance[R]: Bifunctor[ZStream[R, *, *]] =
new CatsBifunctor[R] {}

implicit def zioArrowInstance[E]: ArrowChoice[ZStream[*, E, *]] = new CatsArrow[E]
implicit def zstreamArrowInstance[E]: ArrowChoice[ZStream[*, E, *]] = new CatsArrow[E]
}

sealed abstract class CatsInstances2 extends CatsInstances3 {
implicit def parallelInstance[R, E]: Parallel.Aux[ZStream[R, E, *], ParStream[R, E, *]] =
new CatsParallel[R, E](monadErrorInstance)
implicit def zstreamParallelInstance[R, E]: Parallel.Aux[ZStream[R, E, *], ParStream[R, E, *]] =
new CatsParallel[R, E](zstreamMonadErrorInstance)

implicit def semigroupKInstance[R, E: Semigroup]: SemigroupK[ZStream[R, E, *]] =
implicit def zstreamSemigroupKInstance[R, E: Semigroup]: SemigroupK[ZStream[R, E, *]] =
new CatsSemigroupK[R, E]
}

sealed abstract class CatsInstances3 {
implicit def monadErrorInstance[R, E]: MonadError[ZStream[R, E, *], E] =
implicit def zstreamMonadErrorInstance[R, E]: MonadError[ZStream[R, E, *], E] =
new CatsMonadError[R, E]
}

Expand Down

0 comments on commit 231e005

Please sign in to comment.