Skip to content

Commit

Permalink
Merge pull request #203 from lhns/feature/dotty-cps-async-0.9.20
Browse files Browse the repository at this point in the history
dotty-cps-async 0.9.20
  • Loading branch information
djspiewak authored Mar 13, 2024
2 parents a8d3970 + 12fd89d commit 5e1d8ef
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 39 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)

libraryDependencies ++= {
if (tlIsScala3.value)
Seq("com.github.rssh" %%% "dotty-cps-async" % "0.9.16")
Seq("com.github.rssh" %%% "dotty-cps-async" % "0.9.20")
else
Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided")
})
Expand Down
94 changes: 56 additions & 38 deletions core/shared/src/main/scala-3/cats/effect/cps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,67 +16,85 @@

package cats.effect

import _root_.cps.{async, await, CpsAsyncMonad, CpsAwaitable, CpsConcurrentEffectMonad, CpsMonad, CpsMonadContext, CpsMonadInstanceContext, CpsMonadMemoization}

import cats.effect.kernel.{Async, Concurrent, Fiber, Sync}
import _root_.cps._
import cats.effect.kernel.syntax.all._
import cats.effect.kernel.{Async, Concurrent, Fiber, Sync}
import cats.{Monad, MonadThrow}

import scala.annotation.implicitNotFound
import scala.util.Try

object cps {

transparent inline def async[F[_]](using CpsMonad[F]) = _root_.cps.macros.Async.async[F]
transparent inline def async[F[_]](using CpsMonad[F]) =
_root_.cps.macros.Async.async[F]

extension [F[_], A](self: F[A])(using CpsAwaitable[F], CpsMonadContext[F]) {
extension [F[_], A](self: F[A])(using CpsMonadContext[F]) {
transparent inline def await: A = _root_.cps.await[F, A, F](self)
}

@implicitNotFound("automatic coloring is disabled")
implicit def automaticColoringTag1[F[_]:Concurrent]: _root_.cps.automaticColoring.AutomaticColoringTag[F] = ???
implicit def automaticColoringTag2[F[_]:Concurrent]: _root_.cps.automaticColoring.AutomaticColoringTag[F] = ???
abstract class MonadCpsMonad[F[_]: Monad] extends CpsMonad[F] {
override def pure[T](t: T): F[T] =
Monad[F].pure(t)

// actually not used by dotty-cps-async but need for binary compability with cats-effect-cps 0.4.x.
// TODO: remove in 0.5.0
implicit def catsEffectCpsMonadPureMemoization[F[_]](implicit F: Concurrent[F]): CpsMonadMemoization.Pure[F] =
new CpsMonadMemoization.Pure[F] {
def apply[A](fa: F[A]): F[F[A]] = F.memoize(fa)
}
override def map[A, B](fa: F[A])(f: A => B): F[B] =
Monad[F].map(fa)(f)

override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] =
Monad[F].flatMap(fa)(f)
}

// TODO we can actually provide some more gradient instances here
implicit def catsEffectCpsConcurrentMonad[F[_]](implicit F: Async[F]): CpsConcurrentEffectMonad[F] with CpsMonadInstanceContext[F] =
new CpsConcurrentEffectMonad[F] with CpsMonadInstanceContext[F] {
abstract class MonadThrowCpsMonad[F[_]: MonadThrow]
extends MonadCpsMonad[F]
with CpsTryMonad[F] {
override def error[A](e: Throwable): F[A] =
MonadThrow[F].raiseError(e)

type Spawned[A] = Fiber[F, Throwable, A]
override def flatMapTry[A, B](fa: F[A])(f: Try[A] => F[B]): F[B] =
MonadThrow[F].flatMap(MonadThrow[F].attempt(fa))(e => f(e.toTry))
}

def spawnEffect[A](op: => F[A]): F[Spawned[A]] =
F.defer(op).start
abstract class SyncCpsMonad[F[_]: Sync]
extends MonadThrowCpsMonad[F]
with CpsEffectMonad[F] {
override def delay[T](x: => T): F[T] =
Sync[F].delay(x)

def join[A](op: Spawned[A]): F[A] =
op.joinWithNever
override def flatDelay[T](x: => F[T]): F[T] =
Sync[F].defer(x)
}

abstract class AsyncCpsMonad[F[_]: Async]
extends SyncCpsMonad[F]
with CpsAsyncEffectMonad[F] {
override def adoptCallbackStyle[A](source: (Try[A] => Unit) => Unit): F[A] =
Async[F].async_(f => source(e => f(e.toEither)))
}

def tryCancel[A](op: Spawned[A]): F[Unit] =
op.cancel
abstract class ConcurrentCpsMonad[F[_]: Async: Concurrent]
extends AsyncCpsMonad[F]
with CpsConcurrentEffectMonad[F] {
override type Spawned[A] = Fiber[F, Throwable, A]

override def delay[A](x: => A): F[A] =
Sync[F].delay(x)
override def spawnEffect[A](op: => F[A]): F[Spawned[A]] =
Sync[F].defer(op).start

override def flatDelay[A](x: => F[A]): F[A] =
Sync[F].defer(x)
override def join[A](op: Spawned[A]): F[A] =
op.joinWithNever

def adoptCallbackStyle[A](source: (Try[A] => Unit) => Unit): F[A] =
F.async_(cb => source(t => cb(t.toEither)))
override def tryCancel[A](op: Spawned[A]): F[Unit] =
op.cancel
}

def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f)
implicit def catsMonadCps[F[_]](implicit F: Monad[F]): CpsMonad[F] = new MonadCpsMonad[F]
with CpsPureMonadInstanceContext[F]

def map[A, B](fa: F[A])(f: A => B): F[B] = F.map(fa)(f)
implicit def catsMonadThrowCps[F[_]](implicit F: Async[F]): CpsTryMonad[F] = new MonadThrowCpsMonad[F]
with CpsTryMonadInstanceContext[F]

def pure[A](a: A): F[A] = F.pure(a)
implicit def catsAsyncCps[F[_]](implicit F: Async[F]): CpsAsyncEffectMonad[F] = new AsyncCpsMonad[F]
with CpsAsyncEffectMonadInstanceContext[F]

def error[A](e: Throwable): F[A] = F.raiseError(e)
implicit def catsConcurrentCps[F[_]](implicit F: Async[F], concurrent: Concurrent[F]): CpsConcurrentEffectMonad[F] =
new ConcurrentCpsMonad[F] with CpsConcurrentEffectMonadInstanceContext[F]

def flatMapTry[A,B](fa: F[A])(f: Try[A] => F[B]): F[B] =
F.flatMap(F.attempt(fa))(e => f(e.toTry))
}
}
51 changes: 51 additions & 0 deletions core/shared/src/test/scala-3/cats/effect/MonadAsyncAwaitSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2021-2022 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cats.effect

import cats.syntax.all._
import org.specs2.mutable.Specification
import cps._

class MonadAsyncAwaitSpec extends Specification {

"async[Option]" should {

"be Some" in {

val option = async[Option] {
val a = "a".some.await
val b = Option.when(1 + 1 == 2)("b").await
a + b
}

option must beSome("ab")
}

"be None" in {

val option = async[Option] {
val a = "a".some.await
val b = Option.when(1 + 1 == 3)("b").await
a + b
}

option must beNone
}

}

}

0 comments on commit 5e1d8ef

Please sign in to comment.