Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transformers support for effects #1245

Merged
merged 2 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion modules/core/ce2/src/test/scala/tofu/DelaySuite.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package tofu

import org.scalatest.funsuite.AnyFunSuite
import cats.data.ReaderT
import cats.effect.IO
import org.scalatest.funsuite.AnyFunSuite

class DelaySuite extends AnyFunSuite {
test("IO pure delay") {
Expand All @@ -16,4 +17,25 @@ class DelaySuite extends AnyFunSuite {
}.unsafeRunSync() === 1632)
assert(x === -723)
}

test("Lift delay") {
var x = 154
var y = 1242

def foo[F[_]: Delay](): F[Int] = {
Delay[ReaderT[F, String, _]].delay {
y = 0
42
}.run("str")

Delay[ReaderT[F, String, _]].delay {
x = -723
1632
}.run("str")
}

assert(foo[IO]().unsafeRunSync() === 1632)
assert(x === -723)
assert(y === 1242)
}
}
20 changes: 16 additions & 4 deletions modules/core/ce2/src/test/scala/tofu/time/TimeSuite.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package tofu.time

import cats.data.ReaderT
import cats.effect.{Concurrent, IO, Timer}
import cats.Monad
import cats.data.*
import cats.effect.{Concurrent, ContextShift, IO, Timer}
import tofu.compat.unused

import scala.concurrent.ExecutionContext
import cats.effect.ContextShift
import tofu.compat.unused

@unused
class TimeSuite {
Expand All @@ -26,16 +26,28 @@ class TimeSuite {
Clock[IO]
Sleep[IO]
Timeout[IO]
TimeZone[IO]
}

def readerIO = {
Clock[ReaderT[IO, Unit, _]]
Sleep[ReaderT[IO, Unit, _]]
Timeout[ReaderT[IO, Unit, _]]
TimeZone[ReaderT[IO, Unit, _]]
}

def readerF[F[_]: Clock: Sleep] = {
Clock[ReaderT[F, Unit, _]]
Sleep[ReaderT[F, Unit, _]]
}

def dataF[F[_]: Monad: Clock: Sleep: TimeZone] = {
Clock[WriterT[F, Unit, _]]
Sleep[StateT[F, Unit, _]]
TimeZone[OptionT[F, _]]
Clock[EitherT[F, Unit, _]]
Sleep[IorT[F, Unit, _]]
TimeZone[ContT[F, Unit, _]]
Clock[RWST[F, Unit, Unit, Unit, _]]
}
}
24 changes: 23 additions & 1 deletion modules/core/ce3/src/test/scala/tofu/DelaySuite.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package tofu

import org.scalatest.funsuite.AnyFunSuite
import cats.data.ReaderT
import cats.effect.IO
import cats.effect.unsafe.IORuntime
import org.scalatest.funsuite.AnyFunSuite

class DelaySuite extends AnyFunSuite {
implicit val rt: IORuntime = IORuntime.global
Expand All @@ -19,4 +20,25 @@ class DelaySuite extends AnyFunSuite {
}.unsafeRunSync() === 1632)
assert(x === -723)
}

test("Lift delay") {
var x = 154
var y = 1242

def foo[F[_]: Delay](): F[Int] = {
Delay[ReaderT[F, String, _]].delay {
y = 0
42
}.run("str")

Delay[ReaderT[F, String, _]].delay {
x = -723
1632
}.run("str")
}

assert(foo[IO]().unsafeRunSync() === 1632)
assert(x === -723)
assert(y === 1242)
}
}
16 changes: 14 additions & 2 deletions modules/core/ce3/src/test/scala/tofu/time/TimeSuite.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package tofu.time

import cats.data.ReaderT
import cats.Monad
import cats.data.*
import cats.effect.{IO, Temporal}

import tofu.compat.unused

@unused
Expand All @@ -21,16 +21,28 @@ class TimeSuite {
Clock[IO]
Sleep[IO]
Timeout[IO]
TimeZone[IO]
}

def readerIO = {
Clock[ReaderT[IO, Unit, _]]
Sleep[ReaderT[IO, Unit, _]]
Timeout[ReaderT[IO, Unit, _]]
TimeZone[ReaderT[IO, Unit, _]]
}

def readerF[F[_]: Clock: Sleep] = {
Clock[ReaderT[F, Unit, _]]
Sleep[ReaderT[F, Unit, _]]
}

def dataF[F[_]: Monad: Clock: Sleep: TimeZone] = {
Clock[WriterT[F, Unit, _]]
Sleep[StateT[F, Unit, _]]
TimeZone[OptionT[F, _]]
Clock[EitherT[F, Unit, _]]
Sleep[IorT[F, Unit, _]]
TimeZone[ContT[F, Unit, _]]
Clock[RWST[F, Unit, Unit, Unit, _]]
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package tofu.internal
package instances

import tofu.internal.carriers.ClockCE3Carrier
import tofu.internal.carriers.ClockCE2Carrier
import tofu.higherKind
import tofu.higherKind.RepresentableK
import tofu.internal.carriers.{ClockCE2Carrier, ClockCE3Carrier}
import tofu.time.Clock

private[tofu] trait ClockInstance extends ClockInstance0 {
Expand All @@ -11,4 +12,6 @@ private[tofu] trait ClockInstance extends ClockInstance0 {

private[tofu] trait ClockInstance0 {
implicit def ce2Interop[F[_]](implicit clock: ClockCE2Carrier[F]): Clock[F] = clock

implicit val clockRepresentableK: RepresentableK[Clock] = higherKind.derived.genRepresentableK[Clock]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tofu.internal.instances

import tofu.Delay
import tofu.higherKind.RepresentableK
import tofu.{Delay, higherKind}
import tofu.internal.carriers.DelayCarrier3
import tofu.internal.carriers.DelayCarrier2

Expand All @@ -10,4 +11,6 @@ private[tofu] trait DelayInstance extends DelayInstance0 {

private[tofu] trait DelayInstance0 {
final implicit def interopCE2[F[_]](implicit carrier: DelayCarrier2[F]): Delay[F] = carrier

implicit val delayRepresentableK: RepresentableK[Delay] = higherKind.derived.genRepresentableK[Delay]
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package tofu.internal.instances

import tofu.higherKind
import tofu.higherKind.RepresentableK
import tofu.internal.carriers.{SleepCE2Carrier, SleepCE3Carrier}
import tofu.time.Sleep
import tofu.internal.carriers.SleepCE3Carrier
import tofu.internal.carriers.SleepCE2Carrier

private[tofu] trait SleepInstance extends SleepInstance0 {
implicit def ce3Interop[F[_]](implicit sleep: SleepCE3Carrier[F]): Sleep[F] = sleep
}

private[tofu] trait SleepInstance0 {
implicit def ce2Interop[F[_]](implicit sleep: SleepCE2Carrier[F]): Sleep[F] = sleep

implicit val sleepRepresentableK: RepresentableK[Sleep] = higherKind.derived.genRepresentableK[Sleep]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tofu.internal.instances

import tofu.higherKind
import tofu.higherKind.RepresentableK
import tofu.time.TimeZone

private[tofu] trait TimeZoneInstance {
implicit val timeZoneRepresentableK: RepresentableK[TimeZone] = higherKind.derived.genRepresentableK[TimeZone]
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package tofu.internal
package instances

import cats.~>
import tofu.higherKind.{RepK, RepresentableK}
import tofu.internal.carriers.ClockCE3Carrier
import tofu.internal.carriers.ClockCE2Carrier
import tofu.time.Clock

import java.util.concurrent.TimeUnit
import scala.compiletime.summonFrom

private[tofu] trait ClockInstance:
geny200 marked this conversation as resolved.
Show resolved Hide resolved
inline given [F[_]]: Clock[F] = summonFrom {
case carrier: ClockCE3Carrier[F] => carrier
case carrier: ClockCE2Carrier[F] => carrier
}

// TODO: use higherKind.derived macro when it is ready for scala 3
given clockRepresentableK: RepresentableK[Clock] = new RepresentableK[Clock] {
def tabulate[F[_]](hom: RepK[Clock, _] ~> F): Clock[F] = new Clock[F] {
def realTime(unit: TimeUnit): F[Long] = hom(RepK[Clock](_.realTime(unit)))
def nanos: F[Long] = hom(RepK[Clock](_.nanos))
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package tofu.internal
package instances

import cats.~>
import tofu.Delay
import tofu.higherKind.{RepK, RepresentableK}
import tofu.internal.carriers.{DelayCarrier2, DelayCarrier3}

import scala.compiletime.summonFrom
Expand All @@ -11,3 +13,10 @@ private[tofu] trait DelayInstance:
case carrier: DelayCarrier3[F] => carrier
case carrier: DelayCarrier2[F] => carrier
}

// TODO: use higherKind.derived macro when it is ready for scala 3
given delayRepresentableK: RepresentableK[Delay] = new RepresentableK[Delay] {
def tabulate[F[_]](hom: RepK[Delay, _] ~> F): Delay[F] = new Delay[F] {
def delay[A](a: => A): F[A] = hom(RepK[Delay](_.delay(a)))
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package tofu.internal
package instances

import cats.~>
import tofu.higherKind.{RepK, RepresentableK}
import tofu.internal.carriers.{SleepCE2Carrier, SleepCE3Carrier}
import tofu.time.Sleep

import scala.compiletime.summonFrom
import scala.concurrent.duration.FiniteDuration

private[tofu] trait SleepInstance:
inline given [F[_]]: Sleep[F] =
summonFrom {
case carrier: SleepCE3Carrier[F] => carrier
case carrier: SleepCE2Carrier[F] => carrier
}

// TODO: use higherKind.derived macro when it is ready for scala 3
given sleepRepresentableK: RepresentableK[Sleep] = new RepresentableK[Sleep] {
def tabulate[F[_]](hom: RepK[Sleep, _] ~> F): Sleep[F] = new Sleep[F] {
def sleep(duration: FiniteDuration): F[Unit] = hom(RepK[Sleep](_.sleep(duration)))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package tofu.internal.instances

import cats.~>
import tofu.higherKind.{RepK, RepresentableK}
import tofu.time.TimeZone

import java.time.{ZoneId, ZoneOffset}

trait TimeZoneInstance:
// TODO: use higherKind.derived macro when it is ready for scala 3
given timeZoneRepresentableK: RepresentableK[TimeZone] = new RepresentableK[TimeZone] {
def tabulate[F[_]](hom: RepK[TimeZone, _] ~> F): TimeZone[F] = new TimeZone[F] {
override def system: F[ZoneId] = hom(RepK[TimeZone](_.system))
override def available: F[Set[String]] = hom(RepK[TimeZone](_.available))
override def of(zoneId: String): F[ZoneId] = hom(RepK[TimeZone](_.of(zoneId)))
override def ofOffset(prefix: String, offset: ZoneOffset): F[ZoneId] =
hom(RepK[TimeZone](_.ofOffset(prefix, offset)))
}
}
14 changes: 13 additions & 1 deletion modules/kernel/src/main/scala/tofu/Delay.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package tofu

import cats.data.*
import cats.{Applicative, FlatMap, Functor, Monoid}
import tofu.internal.EffectComp
import tofu.internal.instances.DelayInstance
import tofu.syntax.liftKernel.CatsTaglessLiftSyntax

trait Delay[F[_]] {
def delay[A](a: => A): F[A]
}

object Delay extends DelayInstance with EffectComp[Delay] {
object Delay extends EffectComp[Delay] with DelayInstance {
type Safe[F[_, _]] = Delay[F[Nothing, _]]
type Catch[F[_, _]] = Delay[F[Throwable, _]]

implicit def delayForKleisli[F[_]: Delay, R]: Delay[Kleisli[F, R, _]] = Delay[F].lift
implicit def delayForWriterT[F[_]: Applicative: Delay, R: Monoid]: Delay[WriterT[F, R, _]] = Delay[F].lift
implicit def delayForOptionT[F[_]: Functor: Delay]: Delay[OptionT[F, _]] = Delay[F].lift
implicit def delayForEitherT[F[_]: Functor: Delay, E]: Delay[EitherT[F, E, _]] = Delay[F].lift
implicit def delayForStateT[F[_]: Applicative: Delay, S]: Delay[StateT[F, S, _]] = Delay[F].lift
implicit def delayForIorT[F[_]: Applicative: Delay, L]: Delay[IorT[F, L, _]] = Delay[F].lift
implicit def delayForContT[F[_]: FlatMap: Delay, R]: Delay[ContT[F, R, _]] = Delay[F].lift
implicit def delayForRWST[F[_]: Applicative: Delay, R, L: Monoid, S]: Delay[RWST[F, R, L, S, _]] = Delay[F].lift
}
18 changes: 15 additions & 3 deletions modules/kernel/src/main/scala/tofu/generate/GenRandom.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package tofu.generate

import cats.Functor
import cats.data.*
import cats.{Applicative, FlatMap, Functor, Monoid}
import tofu.Delay
import tofu.internal.EffectComp
import tofu.syntax.liftKernel.CatsTaglessLiftSyntax
import tofu.syntax.monadic.*

import scala.util.Random
Expand All @@ -18,7 +20,7 @@ trait GenRandom[F[_]] {
def nextInt(n: Int): F[Int]
}

object GenRandom extends EffectComp[GenRandom] {
object GenRandom extends EffectComp[GenRandom] with GetRandomInstances {
def nextLong[F[_]](implicit g: GenRandom[F]): F[Long] = g.nextLong
def nextInt[F[_]](n: Int)(implicit g: GenRandom[F]): F[Int] = g.nextInt(n)

Expand All @@ -37,9 +39,19 @@ object GenRandom extends EffectComp[GenRandom] {
for (rnd <- Delay[I].delay(new Random(random()))) yield new ScalaUtil[F](rnd)
}

private class ScalaUtil[F[_]](rnd: Random)(implicit F: Delay[F]) extends GenRandom[F] with GetRandomInstances {
private class ScalaUtil[F[_]](rnd: Random)(implicit F: Delay[F]) extends GenRandom[F] {
def nextLong: F[Long] = F.delay(rnd.nextLong())
def nextInt(max: Int): F[Int] = F.delay(rnd.nextInt(max))
}

implicit def GenRandomForKleisli[F[_]: GenRandom, R]: GenRandom[Kleisli[F, R, _]] = GenRandom[F].lift
implicit def GenRandomForOptionT[F[_]: Functor: GenRandom]: GenRandom[OptionT[F, _]] = GenRandom[F].lift
implicit def GenRandomForEitherT[F[_]: Functor: GenRandom, E]: GenRandom[EitherT[F, E, _]] = GenRandom[F].lift
implicit def GenRandomForStateT[F[_]: Applicative: GenRandom, S]: GenRandom[StateT[F, S, _]] = GenRandom[F].lift
implicit def GenRandomForIorT[F[_]: Applicative: GenRandom, L]: GenRandom[IorT[F, L, _]] = GenRandom[F].lift
implicit def GenRandomForContT[F[_]: FlatMap: GenRandom, R]: GenRandom[ContT[F, R, _]] = GenRandom[F].lift
implicit def GenRandomForWriterT[F[_]: Applicative: GenRandom, R: Monoid]: GenRandom[WriterT[F, R, _]] =
GenRandom[F].lift
implicit def GenRandomForRWST[F[_]: Applicative: GenRandom, R, L: Monoid, S]: GenRandom[RWST[F, R, L, S, _]] =
GenRandom[F].lift
}
Loading
Loading