Getting Started
Effectie
Project | Maven Central |
---|---|
effectie-cats-effect3 | |
effectie-cats-effect | |
effectie-monix | |
effectie-scalaz-effect |
- Supported Scala Versions:
3
,2.13
and2.12
A set of type-classes and utils for functional effect libraries (i.e. Cats Effect, Monix and Scalaz's Effect).
Why Effectie? Please read "Why?" section.
Getting Started
For Cats Effect
In build.sbt
,
- Cats Effect 3
- Cats Effect 2
libraryDependencies += "io.kevinlee" %% "effectie-cats-effect3" % "1.16.0"
libraryDependencies += "io.kevinlee" %% "effectie-cats-effect" % "1.16.0"
then import
import effectie.cats.ConsoleEffectful._
import effectie.cats.Effectful._
import effectie.cats.EitherTSupport._
import effectie.cats.OptionTSupport._
import effectie.cats._
For more details, check out Effectie for Cats Effect.
For Monix
In build.sbt
,
libraryDependencies += "io.kevinlee" %% "effectie-monix" % "1.16.0"
then import
import effectie.monix.ConsoleEffectful._
import effectie.monix.Effectful._
import effectie.monix.EitherTSupport._
import effectie.monix.OptionTSupport._
import effectie.monix._
For more details, check out Effectie for Monix.
For Scalaz Effect
In build.sbt
,
libraryDependencies += "io.kevinlee" %% "effectie-scalaz-effect" % "1.16.0"
then import
import effectie.scalaz.ConsoleEffectful._
import effectie.scalaz.Effectful._
import effectie.scalaz.EitherTSupport._
import effectie.scalaz.OptionTSupport._
import effectie.scalaz._
For more details, check out Effectie for Scalaz Effect.
Why?
Tagless final gives us power to defer the decision of the implementations of contexts we're binding and functional effect libraries like Cats Effect and Scalaz Effect give us referential transparency. There might be an issue though with constructing an effect type data. It is actually an issue with Cats Effect as Cats Effect IO
's pure
(or Monad.pure
) is not referentially transparent.
Let's check out some code examples.
e.g.) Cats Effect
import cats.effect._
// or cats.Monad[IO].pure(println("a"))
val io = IO.pure(println("a"))
// a
// io: IO[Unit] = Pure(a = ())
// It is not referentially transparent so immediately evaluates println("a")
io.unsafeRunSync()
io.unsafeRunSync()
e.g.) Scalaz Effect
import scalaz._, scalaz.effect._
val io = Monad[IO].pure(println("a"))
// io: IO[Unit] = scalaz.effect.IO$$anon$7@22cc02fc
// It is referentially transparent so println("a") is not evaluated here.
io.unsafePerformIO()
// a
io.unsafePerformIO()
// a
So to have referential transparency when using Cats Effect, IO.apply()
should be used.
import cats.effect._
val io = IO(println("a"))
// io: IO[Unit] = Delay(thunk = <function0>)
// Now it is referentially transparent so println("a") is not evaluated here.
io.unsafeRunSync()
// a
io.unsafeRunSync()
// a
Now, let's use Cats Effect with tagless final.
import cats.effect._
trait Foo[F[_]] {
def get[A](a: => A): F[A]
}
class Bar[F[_]] extends Foo[F] {
def get[A](a: => A): F[A] =
// How would you construct F[A]?
}
// call-site
val bar = new Bar[IO]
bar.get(1)
bar.get(println("a"))
How would you construct F[A]
? You could probably do Applicative[F].pure
or Monad[F].pure(a)
.
import cats._
class Bar[F[_]: Applicative] extends Foo[F] { // or [F[_]: Monad]
def get[A](a: => A): F[A] =
Applicative[F].pure(a) // or Monad[F].pure(a)
}
However, neither Applicative.pure
nor Monad.pure
in Cats are referentially transparent when it's mixed with impure code (e.g. some side-effect code like println("a")
).
So If you do this,
val bar = new Bar[IO]
val iou = bar.get(println("a"))
// a is printed here
// and you get IO[Unit]
iou.unsafeRunSync() // This does not print anything but returns ()
With Effectie you can do this.
import cats.effect._
import effectie.cats._
trait Foo[F[_]] {
def get[A](a: => A): F[A]
}
class Bar[F[_]: Fx] extends Foo[F] {
def get[A](a: => A): F[A] =
Fx[F].effectOf(a)
}
// call-site
val bar = new Bar[IO]
// bar: Bar[IO] = repl.MdocSession$App9$Bar@71c4de2c
val iou = bar.get(println("a"))
// iou: IO[Unit] = Delay(thunk = <function0>)
// This does not print anything here.
iou.unsafeRunSync()
// a
iou.unsafeRunSync()
// a
Or a more convenient way like
import cats.effect._
import effectie.cats.Effectful._
import effectie.cats._
trait Foo[F[_]] {
def get[A](a: => A): F[A]
}
class Bar[F[_]: Fx] extends Foo[F] {
def get[A](a: => A): F[A] =
effectOf(a) // no more Fx[F].effectOf
}
// call-site
val bar = new Bar[IO]
// bar: Bar[IO] = repl.MdocSession$App12$Bar@3f0a138
val iou = bar.get(println("a"))
// iou: IO[Unit] = Delay(thunk = <function0>)
// This does not print anything here.
iou.unsafeRunSync()
// a
iou.unsafeRunSync()
// a
Check out