Skip to main content
Version: 2.0.0

Fx

If you use Cats Effect and write tagless final code, and look for a generic way to construct F[A], Fx can help you.

pureOf

To construct F[A] for a pure value A, you can use pureOf.

import effectie.core._
import effectie.syntax.all._

def foo[F[_]: Fx]: F[Int] = pureOf(1)
import cats.effect._
import effectie.syntax.all._

import effectie.instances.ce2.fx._

pureOf[IO](1)
// res3: IO[Int] = Pure(a = 1)

effectOf

To construct F[A] for an operation with referential transparency, you can use effectOf.

import effectie.core._
import effectie.syntax.all._

def foo[F[_]: Fx](): F[Unit] = effectOf(println("Hello"))
import cats.effect._

import effectie.instances.ce2.fx._

(for {
_ <- foo[IO]()
_ <- foo[IO]()
_ <- foo[IO]()
} yield ()).unsafeRunSync()
// Hello
// Hello
// Hello
import cats.effect._
import effectie.syntax.all._

import effectie.instances.ce2.fx._

effectOf[IO](println("Hello"))
// res11: IO[Unit] = Delay(
// thunk = <function0>,
// trace = StackTrace(
// ...

// effectOf can handle exception properly.
effectOf[IO][Int](throw new RuntimeException("ERROR"))
// res12: IO[Int] = Delay(
// thunk = <function0>,
// trace = StackTrace(
// ...

pureOrError

To construct F[A] for a pure value, but it can also throw an exception, you can use pureOrError instead of effectOf.

If an expression returns a pure value, and it's always the same so there's no point in using effectOf for referential transparency, you can use pureOf. However, if that expression can also throw an exception, pureOf cannot handle it properly. In this case, pureOrError is the right one.

e.g.)

val s: String = "abc"
pureOf[IO](s.substring(5))
// This immediately throws a StringIndexOutOfBoundsException even though F[_] here is IO.
val s: String = "abc"
pureOrError[IO](s.substring(5))
// StringIndexOutOfBoundsException is now captured by IO.
import effectie.core._
import effectie.syntax.all._

def foo[F[_]: Fx]: F[Int] = pureOrError(1)
def bar[F[_]: Fx](s: String): F[String] = pureOrError(s.substring(5))
import cats.effect._
import effectie.syntax.all._

import effectie.instances.ce2.fx._

pureOrError[IO](1)
// res19: IO[Int] = Pure(a = 1)

pureOrError[IO]("abc".substring(5))
// res20: IO[String] = RaiseError(
// e = java.lang.StringIndexOutOfBoundsException: String index out of range: -2
// )

unitOf

If you just return F[Unit], you can use unitOf.

e.g.)

unitOf[IO] // IO[Unit]
import cats._
import cats.syntax.all._

import effectie.core._
import effectie.syntax.all._

def foo[F[_]: Fx]: F[Unit] = unitOf

def bar[F[_]: Fx: Monad]: F[Unit] =
effectOf(println("Hello")) *> unitOf // You can do effectOf(println("Hello")).void instead
import cats.effect._
import effectie.syntax.all._

import effectie.instances.ce2.fx._

unitOf[IO]
// res27: IO[Unit] = Pure(a = ())

Example

import effectie.core._
import effectie.syntax.all._

trait Something[F[_]] {
def get[A](a: => A): F[A]
}

object Something {
def apply[F[_]: Something]: Something[F] =
implicitly[Something[F]]

implicit def something[F[_]: Fx]: Something[F] =
new SomethingF[F]

final class SomethingF[F[_]: Fx]
extends Something[F] {

def get[A](a: => A): F[A] =
effectOf(a)
// No more Fx[F].effectOf(a)
}
}

import cats.effect._
import effectie.instances.ce2.fx._

val get1 = Something[IO].get(1)
// get1: IO[Int] = Delay(
// thunk = <function0>,
// trace = StackTrace(
// ...

get1.unsafeRunSync()
// res31: Int = 1