Skip to content

Commit

Permalink
Re-enable macro annotations (for IntelliJ). Re-enable manifest. Add d…
Browse files Browse the repository at this point in the history
…ocs. (#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
  • Loading branch information
deusaquilus authored Feb 16, 2023
1 parent afe0f37 commit 5b3827b
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 30 deletions.
2 changes: 2 additions & 0 deletions annotate-disable.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions annotate-enable.sh
Original file line number Diff line number Diff line change
@@ -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
32 changes: 21 additions & 11 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,28 @@ 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.

## 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.

Expand Down
157 changes: 157 additions & 0 deletions docs/other-supported-monads.md
Original file line number Diff line number Diff line change
@@ -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)
```
2 changes: 1 addition & 1 deletion docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
}
]
};
Expand Down
32 changes: 16 additions & 16 deletions zio-direct/src/main/scala-3.x/zio/direct/Dsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down

0 comments on commit 5b3827b

Please sign in to comment.