Skip to content

Commit 19528af

Browse files
authored
Merge pull request #5 from djspiewak/feature/scala3
Added Scala 3 support
2 parents fcb4b54 + 2c672c9 commit 19528af

File tree

9 files changed

+116
-27
lines changed

9 files changed

+116
-27
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
strategy:
2323
matrix:
2424
os: [ubuntu-latest]
25-
scala: [2.12.14, 2.13.6]
25+
scala: [2.12.14, 2.13.6, 3.0.0]
2626
2727
runs-on: ${{ matrix.os }}
2828
steps:

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@ This is an incubator library for `async`/`await` syntax in Cats Effect, currentl
66

77
"CPS" stands for "[Continuation Passing Style](https://en.wikipedia.org/wiki/Continuation-passing_style)". Related project targeting Scala 3: [rssh/cps-async-connect](https://github.com/rssh/cps-async-connect). This functionality is quite similar to similar functionality in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), [Rust](https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html), [Kotlin](https://kotlinlang.org/docs/composing-suspending-functions.html), and many other languages. The primary difference being that, in this library, the `async` marker is a *lexical block*, whereas in other languages the marker is usually a modifier applied at the function level.
88

9-
Special thanks to [Jason Zaugg](https://github.com/retronym) for his work on the implementation of `-Xasync` within scalac.
9+
Special thanks to [Jason Zaugg](https://github.com/retronym) for his work on the implementation of `-Xasync` within scalac. Also [Ruslan Shevchenko](https://github.com/rssh) for his work on dotty-cps-async.
1010

1111
## Usage
1212

1313
```sbt
1414
libraryDependencies += "org.typelevel" %% "cats-effect-cps" % "<version>"
15-
scalacOptions += "-Xasync" // required to enable compiler support
15+
16+
// if on Scala 2
17+
scalacOptions += "-Xasync" // required to enable compiler support on Scala 2
1618
```
1719

18-
Published for Scala 2.13 and 2.12, cross-build with ScalaJS 1.6. Depends on Cats Effect 3.1.0 or higher.
20+
Published for Scala 2.13, 2.12, and 3.0, cross-build with ScalaJS 1.6. Depends on Cats Effect 3.1.0 or higher. Scala 3 support depends on [dotty-cps-async](https://github.com/rssh/dotty-cps-async) 0.8.1.
1921

2022
## Example
2123

build.sbt

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
name := "cats-effect-cps"
1818

19-
ThisBuild / baseVersion := "0.1"
19+
ThisBuild / baseVersion := "0.2"
2020

2121
ThisBuild / organization := "org.typelevel"
2222
ThisBuild / organizationName := "Typelevel"
@@ -31,9 +31,9 @@ ThisBuild / developers := List(
3131
Developer("djspiewak", "Daniel Spiewak", "@djspiewak", url("https://github.com/djspiewak")),
3232
Developer("baccata", "Olivier Melois", "@baccata", url("https://github.com/baccata")))
3333

34-
ThisBuild / crossScalaVersions := Seq("2.12.14", "2.13.6")
34+
ThisBuild / crossScalaVersions := Seq("2.12.14", "2.13.6", "3.0.0")
3535

36-
val CatsEffectVersion = "3.1.0"
36+
val CatsEffectVersion = "3.1.1"
3737

3838
lazy val root = project.in(file(".")).aggregate(core.jvm, core.js).enablePlugins(NoPublishPlugin)
3939

@@ -42,12 +42,28 @@ lazy val core = crossProject(JVMPlatform, JSPlatform)
4242
.settings(
4343
name := "cats-effect-cps",
4444

45-
scalacOptions += "-Xasync",
45+
scalacOptions ++= {
46+
if (isDotty.value)
47+
Seq()
48+
else
49+
Seq("-Xasync")
50+
},
51+
52+
Compile / unmanagedSourceDirectories +=
53+
(Compile / baseDirectory).value.getParentFile() / "shared" / "src" / "main" / s"scala-${scalaVersion.value.split('.').head}",
54+
55+
Test / unmanagedSourceDirectories +=
56+
(Test / baseDirectory).value.getParentFile() / "shared" / "src" / "main" / s"scala-${scalaVersion.value.split('.').head}",
4657

4758
libraryDependencies ++= Seq(
48-
"org.typelevel" %% "cats-effect-std" % CatsEffectVersion,
49-
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
59+
"org.typelevel" %%% "cats-effect-std" % CatsEffectVersion,
60+
61+
"org.typelevel" %%% "cats-effect" % CatsEffectVersion % Test,
62+
"org.typelevel" %%% "cats-effect-testing-specs2" % "1.1.1" % Test),
5063

51-
"org.typelevel" %% "cats-effect" % CatsEffectVersion % Test,
52-
"org.typelevel" %% "cats-effect-testing-specs2" % "1.1.1" % Test,
53-
"org.specs2" %% "specs2-core" % "4.12.1" % Test))
64+
libraryDependencies ++= {
65+
if (isDotty.value)
66+
Seq("com.github.rssh" %%% "dotty-cps-async" % "0.8.1")
67+
else
68+
Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided")
69+
})

core/shared/src/main/scala/cats/effect/cps.scala renamed to core/shared/src/main/scala-2/cats/effect/cps.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package cats.effect
1919
import scala.annotation.compileTimeOnly
2020

2121
import cats.effect.cpsinternal.AsyncAwaitDsl
22-
import cats.effect.kernel.Async
2322

2423
/**
2524
* WARNING: This construct currently only works on scala 2 (2.12.12+ / 2.13.3+),
@@ -65,7 +64,10 @@ object cps {
6564

6665
final class PartiallyAppliedAsync[F0[_]] {
6766
type F[A] = F0[A]
68-
def apply[A](body: => A)(implicit F: Async[F]): F[A] = macro AsyncAwaitDsl.asyncImpl[F, A]
67+
68+
// don't convert this into an import; it hits a bug in Scala 2.12
69+
def apply[A](body: => A)(implicit F: cats.effect.kernel.Async[F]): F[A] =
70+
macro AsyncAwaitDsl.asyncImpl[F, A]
6971
}
7072
}
7173

core/shared/src/main/scala/cats/effect/cpsinternal/AsyncAwaitDsl.scala renamed to core/shared/src/main/scala-2/cats/effect/cpsinternal/AsyncAwaitDsl.scala

File renamed without changes.

core/shared/src/main/scala/cats/effect/cpsinternal/AsyncAwaitStateMachine.scala renamed to core/shared/src/main/scala-2/cats/effect/cpsinternal/AsyncAwaitStateMachine.scala

File renamed without changes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2021 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cats.effect
18+
19+
import _root_.cps.{async, await, CpsAsyncMonad, CpsAwaitable, CpsMonad, CpsMonadPureMemoization}
20+
21+
import cats.effect.kernel.{Async, Concurrent, Sync}
22+
23+
import scala.util.Try
24+
25+
object cps {
26+
27+
transparent inline def async[F[_]](using inline am: CpsMonad[F], F: Sync[F]): InferAsyncArg[F] =
28+
new InferAsyncArg[F]
29+
30+
final class InferAsyncArg[F[_]](using am: CpsMonad[F], F: Sync[F]) {
31+
transparent inline def apply[A](inline expr: A) =
32+
F.defer(_root_.cps.Async.transform[F, A](expr)(using am))
33+
}
34+
35+
final implicit class AwaitSyntax[F[_], A](val self: F[A]) extends AnyVal {
36+
transparent inline def await(using inline am: CpsAwaitable[F]): A =
37+
_root_.cps.await[F, A](self)
38+
}
39+
40+
implicit def catsEffectCpsMonadPureMemoization[F[_]](implicit F: Concurrent[F]): CpsMonadPureMemoization[F] =
41+
new CpsMonadPureMemoization[F] {
42+
def apply[A](fa: F[A]): F[F[A]] = F.memoize(fa)
43+
}
44+
45+
// TODO we can actually provide some more gradient instances here
46+
implicit def catsEffectCpsConcurrentMonad[F[_]](implicit F: Async[F]): CpsAsyncMonad[F] =
47+
new CpsAsyncMonad[F] {
48+
49+
def adoptCallbackStyle[A](source: (Try[A] => Unit) => Unit): F[A] =
50+
F.async_(cb => source(t => cb(t.toEither)))
51+
52+
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f)
53+
54+
def map[A, B](fa: F[A])(f: A => B): F[B] = F.map(fa)(f)
55+
56+
def pure[A](a: A): F[A] = F.pure(a)
57+
58+
def error[A](e: Throwable): F[A] = F.raiseError(e)
59+
60+
def flatMapTry[A,B](fa: F[A])(f: Try[A] => F[B]): F[B] =
61+
F.flatMap(F.attempt(fa))(e => f(e.toTry))
62+
}
63+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package cats.effect
2+
3+
import org.specs2.mutable.Specification
4+
5+
class AsyncAwaitCompilationSpec extends Specification {
6+
7+
"async[F]" should {
8+
"prevent compilation of await[G, *] calls" in {
9+
val tc = typecheck("async[OptionTIO](IO(1).await)").result
10+
tc must beLike {
11+
case TypecheckError(message) =>
12+
message must contain("expected await to be called on")
13+
message must contain("cats.data.OptionT")
14+
message must contain("but was called on cats.effect.IO[Int]")
15+
}
16+
}
17+
}
18+
}

core/shared/src/test/scala/cats/effect/cps/AsyncAwaitSpec.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import cats.data.{Kleisli, OptionT, WriterT}
2121
import cats.effect.testing.specs2.CatsEffect
2222

2323
import org.specs2.mutable.Specification
24-
import org.specs2.execute._, Typecheck._
2524

2625
import scala.concurrent.duration._
2726

@@ -211,16 +210,6 @@ class AsyncAwaitSpec extends Specification with CatsEffect {
211210

212211
type OptionTIO[A] = OptionT[IO, A]
213212
"async[F]" should {
214-
"prevent compilation of await[G, *] calls" in {
215-
val tc = typecheck("async[OptionTIO](IO(1).await)").result
216-
tc must beLike {
217-
case TypecheckError(message) =>
218-
message must contain("expected await to be called on")
219-
message must contain("cats.data.OptionT")
220-
message must contain("but was called on cats.effect.IO[Int]")
221-
}
222-
}
223-
224213
"respect nested async[G] calls" in {
225214
val optionT = OptionT.liftF(IO(1))
226215

@@ -245,5 +234,4 @@ class AsyncAwaitSpec extends Specification with CatsEffect {
245234
}
246235
}
247236
}
248-
249237
}

0 commit comments

Comments
 (0)