Skip to main content
Version: v1

Getting Started

Effectie Logo Effectie

Build Status Release Status Latest version

ProjectMaven Central
effectie-cats-effect3Maven Central
effectie-cats-effectMaven Central
effectie-monixMaven Central
effectie-scalaz-effectMaven Central
  • Supported Scala Versions: 3, 2.13 and 2.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,

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@6153029e
// 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@140bbf2a
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@7598d4f0
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