Skip to content

Commit

Permalink
Merge pull request #1245 from geny200/feature/lift-effects
Browse files Browse the repository at this point in the history
Add transformers support for effects
  • Loading branch information
dos65 committed May 13, 2024
2 parents 50599da + 6d5dcba commit 6075a37
Show file tree
Hide file tree
Showing 19 changed files with 259 additions and 43 deletions.
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:
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

0 comments on commit 6075a37

Please sign in to comment.