From 5b3827b3848e839b3ff80be0caeecfff04f22d57 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 16 Feb 2023 03:04:23 -0500 Subject: [PATCH] Re-enable macro annotations (for IntelliJ). Re-enable manifest. Add docs. (#44) * Cleanup and more testing * Re-enable compat-generator & improve. Add scripts to enable annotation. * Add docs for ZStream/ZPure and Future/List * One more note --- annotate-disable.sh | 2 + annotate-enable.sh | 2 + build.sbt | 32 ++-- docs/index.md | 16 +- docs/other-supported-monads.md | 157 ++++++++++++++++++ docs/sidebars.js | 2 +- .../src/main/scala-3.x/zio/direct/Dsl.scala | 32 ++-- 7 files changed, 213 insertions(+), 30 deletions(-) create mode 100755 annotate-disable.sh create mode 100755 annotate-enable.sh create mode 100644 docs/other-supported-monads.md diff --git a/annotate-disable.sh b/annotate-disable.sh new file mode 100755 index 0000000..5baf302 --- /dev/null +++ b/annotate-disable.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sed -i 's/@scala.reflect.macros.internal.macroImpl("nothing")/\/\/ @scala.reflect.macros.internal.macroImpl("nothing")/' zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala diff --git a/annotate-enable.sh b/annotate-enable.sh new file mode 100755 index 0000000..8288c5b --- /dev/null +++ b/annotate-enable.sh @@ -0,0 +1,2 @@ +#!/bin/bash +sed -i 's/\/\/ @scala.reflect.macros.internal.macroImpl("nothing")/@scala.reflect.macros.internal.macroImpl("nothing")/' zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala diff --git a/build.sbt b/build.sbt index 7eaa4fa..bc9cf93 100644 --- a/build.sbt +++ b/build.sbt @@ -53,17 +53,27 @@ lazy val `zio-direct` = project .settings(projectModuleSettings) .enablePlugins(BuildInfoPlugin) .settings( - crossScalaVersions := Seq(Scala212, Scala213, ScalaDotty)//, - // TODO can resourceGenerators be added to Package phase? Double check this. - // Compile / resourceGenerators += Def.task { - // val rootFolder = (Compile / resourceManaged).value / "META-INF" - // rootFolder.mkdirs() - // val compatFile = rootFolder / "intellij-compat.json" - // val compatFileContent = s"""{ "artifact": "${(ThisBuild / organization).value} % zio-direct-intellij_2.13 % ${version.value}" }""" - // println(s"--- Writing compat file: ${compatFile} - ${compatFileContent} ---") - // IO.write(compatFile, compatFileContent) - // Seq(compatFile) - // } + crossScalaVersions := Seq(Scala212, Scala213, ScalaDotty), + Compile / resourceGenerators += Def.task { + val rootFolder = (Compile / resourceManaged).value / "META-INF" + rootFolder.mkdirs() + val compatFile = rootFolder / "intellij-compat.json" + val compatFileContent = s"""{ "artifact": "${(ThisBuild / organization).value} % zio-direct-intellij_2.13 % ${version.value}" }""" + + val doWrite = + if (compatFile.exists()) { + val currentContent = IO.read(compatFile) + currentContent != compatFileContent + } else + true + + if (doWrite) { + println(s"--- Writing compat file: ${compatFile} - ${compatFileContent} ---") + IO.write(compatFile, compatFileContent) + } + + Seq(compatFile) + } ) lazy val `zio-direct-test` = project diff --git a/docs/index.md b/docs/index.md index 3dd566a..fc34b9b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,6 +16,17 @@ To use zio-direct, add the following to your `build.sbt` file. libraryDependencies += "dev.zio" %% "zio-direct" % "@VERSION@" ``` +You can also use zio-direct with ZStream and ZPure by importing the following modules. +> Currently this is only supported in Scala 3. + +```scala +// ZStream +libraryDependencies += "dev.zio" %% "zio-direct-streams" % "@VERSION@" +// ZPure +libraryDependencies += "dev.zio" %% "zio-direct-pure" % "@VERSION@" +``` +See the [Other Supported Monads](other-supported-monads) section for more details. + ## IDE Support The preferred IDE to use with ZIO-Direct is Visual Studio Code + Metals. This is because Metals correctly reads the returns from `defer` calls directly from the Scala compiler which is not the case of IntelliJ. To remedy this issue, a Library Extension is provided for ZIO-Direct. See the [IntelliJ Support](intellij-support) section for more details. @@ -23,9 +34,10 @@ The preferred IDE to use with ZIO-Direct is Visual Studio Code + Metals. This is ## Introduction Talk at Functional Scala 2022: -https://www.slideshare.net/deusaquilus/ziodirect-functional-scala-2022 +* Video - https://www.youtube.com/watch?v=DgqfLfzq5eo +* Slides - https://www.slideshare.net/deusaquilus/ziodirect-functional-scala-2022 -ZIO-Direct allows direct style programming with ZIO. This library provides a *syntactic sugar* that is more powerful than for-comprehensions as well as more natural to use. Simply add the `.run` suffix to any ZIO effect in order to retrieve it's value. +ZIO-Direct allows direct style programming with ZIO. This library provides a *syntactic sugar* that is more powerful than for-comprehensions as well as more natural to use. Simply add the `.run` suffix to any ZIO effect in order to retrieve its value. ZIO-Direct works by using macros to rewrite sequential code into flatMap-chains based on the [Monadless](https://github.com/monadless/monadless) paradigm. The values resulting in `.run` calls from the ZIO effects are not actually awaited. Instead, they are rolled-up into a chain of flatMaps. diff --git a/docs/other-supported-monads.md b/docs/other-supported-monads.md new file mode 100644 index 0000000..b3f7302 --- /dev/null +++ b/docs/other-supported-monads.md @@ -0,0 +1,157 @@ +--- +id: other-supported-monads +title: "Other Supported Monads" +sidebar_label: "Other Supported Monads" +--- + +As of RC5, zio-direct now supports ZStream and ZPure as well as `scala.concurrent.Future` and `scala.List`. The latter two are largely fully functional but largely for demonstration purposes. +> Note that all of these are currently only supported in Scala 3. + +## ZStream + +To use zio-direct with ZStream, add the following to your `build.sbt` file. +```scala +libraryDependencies += "dev.zio" %% "zio-direct-streams" % "@VERSION@" +``` + +You can then use zio-direct with ZStream: +```scala +import zio.direct.stream._ + +val out = + defer { + val a = ZStream(1, 2, 3).each + val b = ZStream("foo", "bar").each + (a, b) + } + +out.runCollect +// ZIO.succeed(Chunk((1,"foo"),(1,"bar"),(2,"foo"),(2,"bar"),(3,"foo"),(3,"bar"))) +``` + +Note that if you are also using zio-direct with ZIO, you should rename the `defer` function to avoid conflicts: + +```scala +import zio.direct.stream.{ defer => deferStream, _ } +import zio.direct._ + +// The `run` function of ZStream is called `each` +val outStream: ZStream[Any, Nothing, (Int, String)] = + deferStream { + val a = ZStream(1, 2, 3).each + val b = ZStream("foo", "bar").each + (a, b) + } + +val outZio: ZIO[Any, Nothing, Chunk[(Int, String)]] = + defer { + val a: Chunk[(Int, String)] = outStream.runCollect.run + val b = ZIO.succeed((123, "baz")).run + a :+ b + } + +// Yields: +// ZIO.succeed(Chunk((1,"foo"),(1,"bar"),(2,"foo"),(2,"bar"),(3,"foo"),(3,"bar"),(123, "baz"))) +``` + +## ZPure + +> Note that Metals auto-complete/type-info popups may be sluggish when using ZPure, especially when try/catch constructs are being used. +> In some cases, you may need to wait for a "Loading..." popup message for 20-30 seconds although the actual (bloop) compile time +> will just be a few seconds. + +To use zio-direct with ZPure, add the following to your `build.sbt` file. +```scala +libraryDependencies += "dev.zio" %% "zio-direct-pure" % "@VERSION@" +``` + +In order to use zio-direct with ZPure, you first need to define a `deferWith[W, S]` context which will define the Logging (`W`) and State (`S`) types for your ZPure computation. + +> Due to limitations of Scala 3, you may need to create the state object type in a separate file (or you may get cyclical-dependency compile-time errors). +```scala +val dc = deferWith[String, MyState] +import dc._ + +// The `run` function of ZStream is called `eval` +val out = + defer { + val s1 = ZPure.get[MyState].eval.value + ZPure.set(MyState("bar")).eval + val s2 = ZPure.get[MyState].eval.value + (s1, s2) + } + +out.provideState(MyState("foo")).run +// ("foo", "bar") +``` + +In order to avoid having to specify the state-type over and over again, several helpers are provided (they are imported from `dc._`). +```scala +val dc = deferWith[String, MyState] +import dc._ + +// The `run` function of ZStream is called `eval` +val out = + defer { + val s1 = getState().value + setState(MyState("bar")) + val s2 = getState().value + (s1, s2) + } + +out.provideState(MyState("foo")).run +// ("foo", "bar") +``` + +## List and Future + +Support for Scala's `List` and `Future` objects is provided from zio-direct. + +To use zio-direct with `List` do the following: +```scala +import zio.direct.list._ + +val out = + defer { + val a = List(1, 2, 3) + val b = List("foo", "bar") + (a, b) + } + +// Yields: +// List((1,"foo"),(1,"bar"),(2,"foo"),(2,"bar"),(3,"foo"),(3,"bar")) +``` + +To use zio-direct with `Future` do the following: +```scala +import zio.direct.future._ +import scala.concurrent.ExecutionContext.Implicits.global + +val out = + defer { + Future("a").run match { + case "a" => Future(1).run + case "b" => Future(2).run + } + } + +// Yields: Future(1) +``` + +Note that it is not necessary to implement ExecutionContext.Implicits.global. You can +implicitly pass in any ExecutionContext you want. It just needs to be in-scope when you +call the `defer` function (i.e. `zio.direct.future.defer`). +```scala +import zio.direct.future._ + +def out(implicit ctx: ExecutionContext) = + defer { + Future("a").run match { + case "a" => Future(1).run + case "b" => Future(2).run + } + } + +out(scala.concurrent.ExecutionContext.global) +// Yields: Future(1) +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index dee6d52..1c5a8cf 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -5,7 +5,7 @@ const sidebars = { label: "ZIO Direct Style", collapsed: false, link: { type: "doc", id: "index" }, - items: [ "supported-constructs", "intellij-support" ] + items: [ "supported-constructs", "intellij-support", "other-supported-monads" ] } ] }; diff --git a/zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala b/zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala index c0fa746..002dba4 100644 --- a/zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala +++ b/zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala @@ -39,41 +39,41 @@ trait deferCall[F[_, _, _], F_out, S, W, MM <: MonadModel] { import zio.direct.core.metaprog.Linearity.{Regular => LinReg, Linear => Lin} - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def apply[T](inline value: T): F_out = impl(value, InfoBehavior.Silent, Use, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def info[T](inline value: T): F_out = impl(value, InfoBehavior.Info, Use, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verbose[T](inline value: T): F_out = impl(value, InfoBehavior.Verbose, Use, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verboseTree[T](inline value: T): F_out = impl(value, InfoBehavior.VerboseTree, Use, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def apply[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Silent, params, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def info[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Info, params, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verbose[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Verbose, params, LinReg) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verboseTree[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.VerboseTree, params, LinReg) object linear { - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def apply[T](inline value: T): F_out = impl(value, InfoBehavior.Silent, Use, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def info[T](inline value: T): F_out = impl(value, InfoBehavior.Info, Use, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verbose[T](inline value: T): F_out = impl(value, InfoBehavior.Verbose, Use, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verboseTree[T](inline value: T): F_out = impl(value, InfoBehavior.VerboseTree, Use, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def apply[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Silent, params, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def info[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Info, params, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verbose[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.Verbose, params, Lin) - // @scala.reflect.macros.internal.macroImpl("nothing") + @scala.reflect.macros.internal.macroImpl("nothing") transparent inline def verboseTree[T](inline params: Use)(inline value: T): F_out = impl(value, InfoBehavior.VerboseTree, params, Lin) } }