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
.
- with syntax
- without syntax
import effectie.core._
import effectie.syntax.all._
def foo[F[_]: Fx]: F[Int] = pureOf(1)
import effectie.core._
def foo[F[_]: Fx]: F[Int] = Fx[F].pureOf(1)
- with syntax
- without syntax
import cats.effect._
import effectie.syntax.all._
import effectie.instances.ce2.fx._
pureOf[IO](1)
// res3: IO[Int] = Pure(a = 1)
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
Fx[IO].pureOf(1)
// res5: IO[Int] = Pure(a = 1)
effectOf
To construct F[A]
for an operation with referential transparency, you can use effectOf
.
- with syntax
- without syntax
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 effectie.core._
def foo[F[_]: Fx](): F[Unit] = Fx[F].effectOf(println("Hello"))
import cats.effect._
import effectie.instances.ce2.fx._
(for {
_ <- foo[IO]()
_ <- foo[IO]()
_ <- foo[IO]()
} yield ()).unsafeRunSync()
// Hello
// Hello
// Hello
- with syntax
- without syntax
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(
// ...
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
Fx[IO].effectOf(println("Hello"))
// res14: IO[Unit] = Delay(
// thunk = <function0>,
// trace = StackTrace(
// ...
// effectOf can handle exception properly.
Fx[IO].effectOf[Int](throw new RuntimeException("ERROR"))
// res15: 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.
- with syntax
- without syntax
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 effectie.core._
def foo[F[_]: Fx]: F[Int] = Fx[F].pureOrError(1)
def bar[F[_]: Fx](s: String): F[String] = Fx[F].pureOrError(s.substring(5))
- with syntax
- without syntax
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
// )
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
Fx[IO].pureOrError(1)
// res22: IO[Int] = Pure(a = 1)
Fx[IO].pureOrError("abc".substring(5))
// res23: 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]
- with syntax
- without syntax
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._
import cats.syntax.all._
import effectie.core._
def foo[F[_]: Fx]: F[Unit] = Fx[F].unitOf
def bar[F[_]: Fx: Monad]: F[Unit] =
Fx[F].effectOf(println("Hello")) *> Fx[F].unitOf // You can do Fx[F].effectOf(println("Hello")).void instead
- with syntax
- without syntax
import cats.effect._
import effectie.syntax.all._
import effectie.instances.ce2.fx._
unitOf[IO]
// res27: IO[Unit] = Pure(a = ())
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
Fx[IO].unitOf
// res29: IO[Unit] = Pure(a = ())
Example
- with syntax
- without syntax
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
import effectie.core._
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] =
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()
// res33: Int = 1