diff --git a/generated-doc/out/adr/0002-retries.md b/generated-doc/out/adr/0002-retries.md index 108e934c..93a839d5 100644 --- a/generated-doc/out/adr/0002-retries.md +++ b/generated-doc/out/adr/0002-retries.md @@ -4,7 +4,7 @@ Date: 2023-11-30 ## Context -How should the [retries API](../retries.md) be implemented in terms of: +How should the [retries API](../utils/retries.md) be implemented in terms of: - developer friendliness, - supported ways of representing the operation under retry, - possibly infinite retries. diff --git a/generated-doc/out/adr/0008-scheduled-repeat-retry.md b/generated-doc/out/adr/0008-scheduled-repeat-retry.md index f3faca16..2cab22b8 100644 --- a/generated-doc/out/adr/0008-scheduled-repeat-retry.md +++ b/generated-doc/out/adr/0008-scheduled-repeat-retry.md @@ -4,11 +4,11 @@ Date: 2024-07-09 ## Context -How should the [retries](../retries.md) and [repeat](../repeat.md) APIs have the common implementation. +How should the [retries](../utils/retries.md) and [repeat](../utils/repeat.md) APIs have the common implementation. ## Decision -We're introducing [scheduled](../scheduled.md) as a common API for both retries and repeats. +We're introducing [scheduled](../utils/scheduled.md) as a common API for both retries and repeats. In addition, `Schedule` trait and its implementations are decoupled from the retry DSL, so that they can be used for repeating as well. `retry` API remains unchanged, but it now uses `scheduled` underneath. diff --git a/generated-doc/out/basics/error-handling.md b/generated-doc/out/basics/error-handling.md index f1180b66..9291a6a7 100644 --- a/generated-doc/out/basics/error-handling.md +++ b/generated-doc/out/basics/error-handling.md @@ -144,7 +144,8 @@ in the return type of a method, another `either:` block will be selected by the change in execution semantics without a compile error. Consider: ```scala -import ox.either, either.* +import ox.either +import ox. either.* def returnsEither: Either[String, Int] = ??? @@ -160,7 +161,8 @@ Now, after a small refactor of `returnsEither` return type the `returnsEither.ok instead of short-circuiting the inner `either:` block, it would immediately jump to the outer `either:` block on errors. ```scala -import ox.either, either.* +import ox.either +import ox.either.* def returnsEither: Either[Exception, Int] = ??? @@ -175,7 +177,8 @@ val outerResult: Either[Exception, Unit] = either: Proper way to solve this is to extract the inner `either:` block to a separate function: ```scala -import ox.either, either.* +import ox.either +import ox.either.* def returnsEither: Either[String, Int] = ??? diff --git a/generated-doc/out/basics/quick-example.md b/generated-doc/out/basics/quick-example.md deleted file mode 100644 index a4f61d57..00000000 --- a/generated-doc/out/basics/quick-example.md +++ /dev/null @@ -1,59 +0,0 @@ -# Quick example - -Below is a quick example of some of Ox's APIs in action. Dive into the specific documentation sections for more details, -variants and functionalities! - -```scala -import ox.* -import ox.either.ok -import ox.channels.* -import ox.flow.Flow -import ox.resilience.* -import ox.scheduling.* -import scala.concurrent.duration.* - -// run two computations in parallel -def computation1: Int = { sleep(2.seconds); 1 } -def computation2: String = { sleep(1.second); "2" } -val result1: (Int, String) = par(computation1, computation2) -// (1, "2") - -// timeout a computation -def computation: Int = { sleep(2.seconds); 1 } -val result2: Either[Throwable, Int] = either.catching(timeout(1.second)(computation)) - -// structured concurrency & supervision -supervised { - forkUser: - sleep(1.second) - println("Hello!") - - forkUser: - sleep(500.millis) - throw new RuntimeException("boom!") -} -// on exception, ends the scope & re-throws - -// retry a computation -def computationR: Int = ??? -retry(RetryConfig.backoff(3, 100.millis, 5.minutes, Jitter.Equal))(computationR) - -// create a flow & transform using high-level operations -Flow.iterate(0)(_ + 1) // natural numbers - .filter(_ % 2 == 0) - .map(_ + 1) - .take(10) - .runForeach(n => println(n.toString)) - -// select from a number of channels -val c = Channel.rendezvous[Int] -val d = Channel.rendezvous[Int] -select(c.sendClause(10), d.receiveClause) - -// unwrap eithers and combine errors in a union type -val v1: Either[Int, String] = ??? -val v2: Either[Long, String] = ??? - -val result: Either[Int | Long, String] = either: - v1.ok() ++ v2.ok() -``` diff --git a/generated-doc/out/basics/start-here.md b/generated-doc/out/basics/start-here.md deleted file mode 100644 index 448411f4..00000000 --- a/generated-doc/out/basics/start-here.md +++ /dev/null @@ -1,64 +0,0 @@ -# Start here - -## Adding Ox to your project - -```scala -// sbt dependency -"com.softwaremill.ox" %% "core" % "0.5.2" - -// scala-cli dependency -//> using dep com.softwaremill.ox::core:0.5.2 -``` - -## Scope of the Ox project - -The areas that we'd like to cover with Ox are: - -* concurrency: developer-friendly structured concurrency, high-level concurrency operators, safe low-level primitives, - communication between concurrently running computations -* error management: retries, timeouts, a safe approach to error propagation, safe resource management -* scheduling & timers -* resiliency: circuit breakers, bulkheads, rate limiters, backpressure - -All of the above should allow for observability of the orchestrated business logic. We aim to enable writing simple, -expression-oriented code in functional style. We'd like to keep the syntax overhead to a minimum, preserving -developer-friendly stack traces, and without compromising performance. - -Some of the above are already addressed in the API, some are coming up in the future. We'd love your help in shaping the -project! - -## Community - -If you'd have feedback, development ideas or critique, please head to our [community forum](https://softwaremill.community/c/ox/12)! -Alternatively, you can create an issue or submit a pull request on [GitHub](https://github.com/softwaremill/ox). - -## Sponsors - -Development and maintenance of Ox is sponsored by [SoftwareMill](https://softwaremill.com), a software development and consulting company. -We help clients scale their business through software. Our areas of expertise include backends, distributed systems, -machine learning and data analytics. - -[![](https://files.softwaremill.com/logo/logo.png "SoftwareMill")](https://softwaremill.com) - -## Commercial Support - -We offer commercial support for Ox and related technologies, as well as development services. -[Contact us](https://softwaremill.com/contact/) to learn more about our offer! - -## Introductory articles - -* [Prototype Loom-based concurrency API for Scala](https://softwaremill.com/prototype-loom-based-concurrency-api-for-scala/) -* [Go-like channels using project Loom and Scala](https://softwaremill.com/go-like-channels-using-project-loom-and-scala/) -* [Two types of futures](https://softwaremill.com/two-types-of-futures/) -* [Supervision, Kafka and Java 21: what’s new in Ox](https://softwaremill.com/supervision-kafka-and-java-21-whats-new-in-ox/) -* [Designing a (yet another) retry API](https://softwaremill.com/designing-a-yet-another-retry-api/) -* [Handling errors in direct-style Scala](https://softwaremill.com/handling-errors-in-direct-style-scala/) -* [Direct-style concurrent streaming](https://softwaremill.com/direct-style-concurrent-streaming/) - -## Inspiration & building blocks - -* [Project Loom](https://openjdk.org/projects/loom/) (virtual threads) -* structured concurrency Java APIs ([JEP 428](https://openjdk.org/jeps/428)) -* scoped values ([JEP 429](https://openjdk.org/jeps/429)) -* fast, scalable [Go](https://golang.org)-like channels using [jox](https://github.com/softwaremill/jox) -* the [Scala 3](https://www.scala-lang.org) programming language diff --git a/generated-doc/out/index.md b/generated-doc/out/index.md index 9017660b..9627131d 100644 --- a/generated-doc/out/index.md +++ b/generated-doc/out/index.md @@ -2,26 +2,33 @@ Safe direct-style concurrency and resiliency for Scala on the JVM. Requires JDK 21 & Scala 3. -To start using Ox, add the `core` dependency as described below. Then, follow one of the topics listed in the menu -to get to know Ox's API. +To start using Ox, add the `com.softwaremill.ox::core:0.5.2` [dependency](info/dependency.md) to your project. +Then, take a look at the tour of Ox, or follow one of the topics listed in the menu to get to know Ox's API! In addition to this documentation, ScalaDocs can be browsed at [https://javadoc.io](https://www.javadoc.io/doc/com.softwaremill.ox). -```{eval-rst} -.. include:: basics/start-here.md - :parser: markdown +```{include} tour.md + ``` ## Table of contents ```{eval-rst} + +.. toctree:: + :maxdepth: 2 + :caption: Project info + + info/community-support + info/dependency + info/scope + .. toctree:: :maxdepth: 2 :caption: Basics - basics/start-here + tour basics/direct-style - basics/quick-example basics/error-handling .. toctree:: @@ -60,28 +67,29 @@ In addition to this documentation, ScalaDocs can be browsed at [https://javadoc. :maxdepth: 2 :caption: Resiliency & utilities - oxapp - retries - repeat - scheduled - resources - control-flow - actors - utility + utils/oxapp + utils/retries + utils/repeat + utils/scheduled + utils/resources + utils/control-flow + utils/actors + utils/utility .. toctree:: :maxdepth: 2 :caption: Integrations - kafka - mdc-logback + integrations/kafka + integrations/mdc-logback .. toctree:: :maxdepth: 2 :caption: Other topics - dictionary - best-practices - performance - compare-gears - compare-funeff + other/links + other/dictionary + other/best-practices + other/performance + other/compare-gears + other/compare-funeff diff --git a/generated-doc/out/info/community-support.md b/generated-doc/out/info/community-support.md new file mode 100644 index 00000000..f6159d54 --- /dev/null +++ b/generated-doc/out/info/community-support.md @@ -0,0 +1,19 @@ +# Community & support + +## Community + +If you'd have feedback, development ideas or critique, please head to our [community forum](https://softwaremill.community/c/ox/12)! +Alternatively, you can create an issue or submit a pull request on [GitHub](https://github.com/softwaremill/ox). + +## Sponsors + +Development and maintenance of Ox is sponsored by [SoftwareMill](https://softwaremill.com), a software development and consulting company. +We help clients scale their business through software. Our areas of expertise include backends, distributed systems, +machine learning and data analytics. + +[![](https://files.softwaremill.com/logo/logo.png "SoftwareMill")](https://softwaremill.com) + +## Commercial Support + +We offer commercial support for Ox and related technologies, as well as development services. +[Contact us](https://softwaremill.com/contact/) to learn more about our offer! \ No newline at end of file diff --git a/generated-doc/out/info/dependency.md b/generated-doc/out/info/dependency.md new file mode 100644 index 00000000..09e93615 --- /dev/null +++ b/generated-doc/out/info/dependency.md @@ -0,0 +1,15 @@ +# Dependency (sbt, scala-cli, etc.) + +To use ox core in your project, add: + +```scala +// sbt dependency +"com.softwaremill.ox" %% "core" % "0.5.2" + +// scala-cli dependency +//> using dep com.softwaremill.ox::core:0.5.2 +``` + +Ox core depends only on the Java [jox](https://github.com/softwaremill/jox) project, where channels are implemented. There are no other direct or transitive dependencies. + +Integration modules have separate dependencies. \ No newline at end of file diff --git a/generated-doc/out/info/scope.md b/generated-doc/out/info/scope.md new file mode 100644 index 00000000..b9fcab1d --- /dev/null +++ b/generated-doc/out/info/scope.md @@ -0,0 +1,24 @@ +# Project scope + +The areas that we'd like to cover with Ox are: + +* concurrency: developer-friendly structured concurrency, high-level concurrency operators, safe low-level primitives, + communication between concurrently running computations +* error management: retries, timeouts, a safe approach to error propagation, safe resource management +* scheduling & timers +* resiliency: circuit breakers, bulkheads, rate limiters, backpressure + +All of the above should allow for observability of the orchestrated business logic. We aim to enable writing simple, +expression-oriented code in functional style. We'd like to keep the syntax overhead to a minimum, preserving +developer-friendly stack traces, and without compromising performance. + +Some of the above are already addressed in the API, some are coming up in the future. We'd love your help in shaping the +project! + +## Inspiration & building blocks + +* [Project Loom](https://openjdk.org/projects/loom/) (virtual threads) +* structured concurrency Java APIs ([JEP 428](https://openjdk.org/jeps/428)) +* scoped values ([JEP 429](https://openjdk.org/jeps/429)) +* fast, scalable [Go](https://golang.org)-like channels using [jox](https://github.com/softwaremill/jox) +* the [Scala 3](https://www.scala-lang.org) programming language diff --git a/generated-doc/out/kafka.md b/generated-doc/out/integrations/kafka.md similarity index 100% rename from generated-doc/out/kafka.md rename to generated-doc/out/integrations/kafka.md diff --git a/generated-doc/out/mdc-logback.md b/generated-doc/out/integrations/mdc-logback.md similarity index 93% rename from generated-doc/out/mdc-logback.md rename to generated-doc/out/integrations/mdc-logback.md index c23bd021..ca912186 100644 --- a/generated-doc/out/mdc-logback.md +++ b/generated-doc/out/integrations/mdc-logback.md @@ -20,7 +20,7 @@ The best place would be the application's entrypoint (the `main` method). Once this is done, inheritable MDC values can be set in a scoped & structured manner using `InheritableMDC.supervisedWhere` and variants. -As inheritable MDC values use a [`ForkLocal`](structured-concurrency/fork-local.md) under the hood, their usage +As inheritable MDC values use a [`ForkLocal`](../structured-concurrency/fork-local.md) under the hood, their usage restrictions apply: outer concurrency scopes should not be used to create forks within the scopes. Only newly created scopes, or the provided scope can be used to create forks. That's why `supervisedError`, `unsupervisedError` and `supervisedErrorWhere` methods are provided. diff --git a/generated-doc/out/best-practices.md b/generated-doc/out/other/best-practices.md similarity index 90% rename from generated-doc/out/best-practices.md rename to generated-doc/out/other/best-practices.md index f8116f48..e68bc6b4 100644 --- a/generated-doc/out/best-practices.md +++ b/generated-doc/out/other/best-practices.md @@ -8,7 +8,7 @@ practices which might be useful for anybody starting their journey with direct-s If you end up using concurrency scopes such as `supervised`, make sure that their lifetime is as short as possible. In some cases it might be necessary to start a "global" scope (e.g. for application-wide, long-running tasks), but even if so, don't let the global scope leak to any other parts of your code, and isolate its usage, e.g. using -[actors](actors.md). +[actors](../utils/actors.md). For all other tasks, create short-lived scopes, which handle a single request, message from a queue or a single job instance. @@ -16,7 +16,7 @@ instance. ## Integrate with callback-based APIs using channels Callback-based APIs, including "reactive" ones, are by their nature non-structured, and don't play well with -structured concurrency. For such cases, [channels](streaming/channels.md) are an ideal tool. Sending or receiving to/from +structured concurrency. For such cases, [channels](../streaming/channels.md) are an ideal tool. Sending or receiving to/from a channel doesn't require any context, and can be done from any thread. On the other hand, processing the data that is on the channel often involves concurrency and creating thread, which can be then done in a structured way. @@ -36,7 +36,7 @@ Transforming channels directly might lead to excessive concurrency, as each tran background fork, processing the data and sending it to a new channel. While this still performs well, as creating virtual threads & channels is cheap, it might incur an unnecessary overhead. -Instead, you can use [flows](streaming/flows.md) and their high-level API, which allows inserting asynchronous +Instead, you can use [flows](../streaming/flows.md) and their high-level API, which allows inserting asynchronous boundaries when necessary, but otherwise runs the subsequent processing stages on the same thread. ## Avoid returning `Fork` diff --git a/generated-doc/out/compare-funeff.md b/generated-doc/out/other/compare-funeff.md similarity index 100% rename from generated-doc/out/compare-funeff.md rename to generated-doc/out/other/compare-funeff.md diff --git a/generated-doc/out/compare-gears.md b/generated-doc/out/other/compare-gears.md similarity index 93% rename from generated-doc/out/compare-gears.md rename to generated-doc/out/other/compare-gears.md index 223bc81d..7b7f252c 100644 --- a/generated-doc/out/compare-gears.md +++ b/generated-doc/out/other/compare-gears.md @@ -13,6 +13,6 @@ Originally [posted on Reddit](https://www.reddit.com/r/scala/comments/1cdfaki/co 5. The programming styles that both libraries offer are slightly different as well. In Gears, most of the provided functionalities operate on the level of Gears-`Future`s. While in Ox, you often operate on thunks `=> T` or `() => T`. The Gears approach is more general, however the Ox one is more "direct" for the common case. 6. That might be considered a small detail, but we think naming is important. Gears is centered around a `Future` abstraction, which is a distinct type from `scala.concurrent.Future`. This might create unnecessary confusion, plus in our opinion it's worthy to distinguish between [promise-like futures and thread-like futures](https://softwaremill.com/two-types-of-futures/). That's why in Ox we've got `Fork`s instead (which are a thread-like-future data type). However, you don't use that type often, because of (5) above. 7. Another rather fundamental difference is in our approach to error handling. In Ox, when you create a `supervised` scope with `fork`s inside, if any of the fork fails, the whole scope fails and re-throws this exception (a variant of let-it-crash). In Gears, the default is to have failed futures, and the errors are only discovered when `.join`ing them. Both approaches have their merits, however the Ox one, where you have to explicitly create unsupervised forks (using `forkUnsupervised`), seems safer: your code might crash, but you won't miss an error. -8. Speaking of error handling, Ox provides support for various `ErrorMode`s, that is situations where you want to represent errors-as-values (in addition to exceptions). We [propose a specific way](basics/error-handling.md) to represent such application/logical errors (using `Either`s), with the built-in concurrency operators often having `Either`-variants, and by providing a boundary-break implementation for `Either`s. Gears might be getting something similar, so this might stop being an actual difference. +8. Speaking of error handling, Ox provides support for various `ErrorMode`s, that is situations where you want to represent errors-as-values (in addition to exceptions). We [propose a specific way](../basics/error-handling.md) to represent such application/logical errors (using `Either`s), with the built-in concurrency operators often having `Either`-variants, and by providing a boundary-break implementation for `Either`s. Gears might be getting something similar, so this might stop being an actual difference. 9. In Gears, there are two capabilities: `Async` and `Async.Spawn`. The first one represents a capability to suspend, the second - to fork. In Ox, we only have the `Ox` capability, which corresponds to `Async.Spawn`. You don't need a capability to suspend. This might be seen as a feature, or a bug. On one hand, it might be useful to know that somewhere down the call chain, your code will want to suspend, and more importantly, to be interrupted. That's the kind of information you get with `Async` in Gears. On the other hand, our worry is that `using Async` will be the new `implicit ec: ExecutionContext`. We're not ruling out adding an `Async`-like capability to Ox, it might turn out to be the right thing to do, but we'd still like to explore some other options (as hinted in the [Scalar talk](https://www.youtube.com/watch?v=C3j4AsFcxmM), near the end) 10. Ox's Kotlin-inspired `Channel` & `select` [implementation](https://github.com/softwaremill/jox) is less flexible than the one in Gears (you can't nest select's that easily), however it might be more performant. Initial benchmarks of a WebSocket server using Java 21 & Ox streaming turned out better or matching the asynchronous implementations. diff --git a/generated-doc/out/dictionary.md b/generated-doc/out/other/dictionary.md similarity index 100% rename from generated-doc/out/dictionary.md rename to generated-doc/out/other/dictionary.md diff --git a/generated-doc/out/other/links.md b/generated-doc/out/other/links.md new file mode 100644 index 00000000..931fa6bc --- /dev/null +++ b/generated-doc/out/other/links.md @@ -0,0 +1,15 @@ +# Blogs, videos, ... + +## Blogs + +* [Prototype Loom-based concurrency API for Scala](https://softwaremill.com/prototype-loom-based-concurrency-api-for-scala/) +* [Go-like channels using project Loom and Scala](https://softwaremill.com/go-like-channels-using-project-loom-and-scala/) +* [Two types of futures](https://softwaremill.com/two-types-of-futures/) +* [Supervision, Kafka and Java 21: what’s new in Ox](https://softwaremill.com/supervision-kafka-and-java-21-whats-new-in-ox/) +* [Designing a (yet another) retry API](https://softwaremill.com/designing-a-yet-another-retry-api/) +* [Handling errors in direct-style Scala](https://softwaremill.com/handling-errors-in-direct-style-scala/) +* [Direct-style concurrent streaming](https://softwaremill.com/direct-style-concurrent-streaming/) + +## Videos + +Coming up! \ No newline at end of file diff --git a/generated-doc/out/performance.md b/generated-doc/out/other/performance.md similarity index 100% rename from generated-doc/out/performance.md rename to generated-doc/out/other/performance.md diff --git a/generated-doc/out/streaming/flows.md b/generated-doc/out/streaming/flows.md index 01badc06..4dc7a391 100644 --- a/generated-doc/out/streaming/flows.md +++ b/generated-doc/out/streaming/flows.md @@ -4,6 +4,10 @@ A `Flow[T]` describes an asynchronous data transformation pipeline. When run, it Flows are lazy, evaluation (and any effects) happen only when the flow is run. Flows might be finite or infinite; in the latter case running a flow never ends normally; it might be interrupted, though. Finally, any exceptions that occur when evaluating the flow's logic will be thrown when running the flow, after any cleanup logic completes. +```{note} +An introduction to Ox's Flow, along with some code samples is available [as a video](https://www.youtube.com/watch?v=2sZGVRXP9PM). +``` + ## Creating flows There's a number of methods on the `Flow` companion object that can be used to create a flow: diff --git a/generated-doc/out/tour.md b/generated-doc/out/tour.md new file mode 100644 index 00000000..5a961463 --- /dev/null +++ b/generated-doc/out/tour.md @@ -0,0 +1,182 @@ +# A tour of ox + + +Run two computations [in parallel](high-level-concurrency/par.md): + +```scala +def computation1: Int = { sleep(2.seconds); 1 } +def computation2: String = { sleep(1.second); "2" } +val result1: (Int, String) = par(computation1, computation2) +// (1, "2") +``` + +[Timeout](high-level-concurrency/timeout.md) a computation: + +```scala +def computation3: Int = { sleep(2.seconds); 1 } +val result2: Either[Throwable, Int] = either.catching(timeout(1.second)(computation3)) +// `timeout` only completes once the loosing branch is interrupted & done +``` + +[Race](high-level-concurrency/race.md) two computations: + +```scala +def computation4: Int = { sleep(2.seconds); 1 } +def computation5: Int = { sleep(1.second); 2 } +val result3: Int = raceSuccess(computation4, computation5) +// as before, the loosing branch is interrupted & awaited before returning a result +``` + +[Structured concurrency](structured-concurrency/fork-join.md) & supervision: + +```scala +// equivalent of par +supervised { + val f1 = fork { sleep(2.seconds); 1 } + val f2 = fork { sleep(1.second); 2 } + (f1.join(), f2.join()) +} +``` + +Error handling within a structured concurrency scope: + +```scala +supervised { + forkUser: + sleep(1.second) + println("Hello!") + + forkUser: + sleep(500.millis) + throw new RuntimeException("boom!") +} +``` + +[Retry](utils/retries.md) a computation: + +```scala +def computationR: Int = ??? +retry(RetryConfig.backoff(3, 100.millis, 5.minutes, Jitter.Equal))(computationR) +``` + +[Repeat](utils/repeat.md) a computation: + +```scala +def computationR: Int = ??? +repeat(RepeatConfig.fixedRateForever(100.millis))(computationR) +``` + +Allocate a [resource](utils/resources.md) in a scope: + +```scala +supervised { + val writer = useCloseableInScope(new java.io.PrintWriter("test.txt")) + // ... use writer ... +} // writer is closed when the scope ends (successfully or with an error) +``` + +[Create an app](utils/oxapp.md) which shuts down cleanly when interrupted with SIGINT/SIGTERM: + +```scala +object MyApp extends OxApp: + def run(args: Vector[String])(using Ox): ExitCode = + // ... your app's code ... + // might use fork {} to create top-level background threads + ExitCode.Success +``` + +Simple type-safe [actors](utils/actors.md): + +```scala +class Stateful { def increment(delta: Int): Int = ??? } + +supervised: + val ref = Actor.create(new Stateful) + // ref can be shared across forks, but only within the concurrency scope + ref.ask(_.increment(5)) +``` + +Create a simple [flow](streaming/flows.md) & transform using a functional API: + +```scala +Flow.iterate(0)(_ + 1) // natural numbers + .filter(_ % 2 == 0) + .map(_ + 1) + .intersperse(5) + // compute the running total + .mapStateful(() => 0) { (state, value) => + val newState = state + value + (newState, newState) + } + .take(10) + .runForeach(n => println(n.toString)) +``` + +Create flows which perform I/O and manage concurrency: + +```scala +def sendHttpRequest(entry: String): Unit = ??? +Flow + .fromInputStream(this.getClass().getResourceAsStream("/list.txt")) + .linesUtf8 + .mapPar(4)(sendHttpRequest) + .runDrain() +``` + +Merge two flows, properly handling the failure of either branches: + +```scala +val f1 = Flow.tick(123.millis, "left") +val f2 = Flow.tick(312.millis, "right") +f1.merge(f2).take(100).runForeach(println) +``` + +Integrate flow with other components using an imperative API: + +```scala +def readNextBatch(): List[String] = ??? +Flow.usingEmit { emit => + forever: + readNextBatch().foreach(emit.apply) +} +``` + +Use completable high-performance [channels](streaming/channels.md) for inter-fork communication within concurrency scopes: + +```scala +val c = Channel.buffered[String](8) +c.send("Hello,") +c.send("World") +c.done() +``` + +[Select](streaming/selecting-from-channels.md) from Go-like channels: + +```scala +val c = Channel.rendezvous[Int] +val d = Channel.rendezvous[Int] +select(c.sendClause(10), d.receiveClause) +``` + +[Unwrap eithers](basics/error-handling.md) and combine errors in a union type: + +```scala +val v1: Either[Int, String] = ??? +val v2: Either[Long, String] = ??? + +val result: Either[Int | Long, String] = either: + v1.ok() ++ v2.ok() +``` + +[Pipe & tap](utils/control-flow.md) values to functions to use the dot-syntax: + +```scala +def compute: Int = ??? +def computeMore(v: Int): Long = ??? +compute + .pipe(2 * _) + .tap(println) + .pipe(computeMore) +``` + +Dive into the specific documentation sections for more details, variants and functionalities! diff --git a/generated-doc/out/actors.md b/generated-doc/out/utils/actors.md similarity index 96% rename from generated-doc/out/actors.md rename to generated-doc/out/utils/actors.md index 022fbf5d..bb660d30 100644 --- a/generated-doc/out/actors.md +++ b/generated-doc/out/utils/actors.md @@ -4,8 +4,8 @@ Actors in Ox enable invoking methods on an object serially, keeping the behavior invocation. That is, even though invocations may happen from multiple threads, they are guaranteed to happen one after the other, not concurrently. -Actor invocations are fully type-safe, with minimal overhead. They use [channels](streaming/channels.md) and -[scopes](structured-concurrency/fork-join.md) behind the scenes. +Actor invocations are fully type-safe, with minimal overhead. They use [channels](../streaming/channels.md) and +[scopes](../structured-concurrency/fork-join.md) behind the scenes. One of the use-cases is integrating with external APIs, which are represented by an object containing mutable state. Such integrations must be protected and cannot be accessed by multiple threads concurrently. diff --git a/generated-doc/out/control-flow.md b/generated-doc/out/utils/control-flow.md similarity index 100% rename from generated-doc/out/control-flow.md rename to generated-doc/out/utils/control-flow.md diff --git a/generated-doc/out/oxapp.md b/generated-doc/out/utils/oxapp.md similarity index 97% rename from generated-doc/out/oxapp.md rename to generated-doc/out/utils/oxapp.md index 7b6e41d1..134ab62f 100644 --- a/generated-doc/out/oxapp.md +++ b/generated-doc/out/utils/oxapp.md @@ -58,7 +58,7 @@ object MyApp extends OxApp.Simple: def run(using Ox): Unit = println("All done!") ``` -`OxApp` has also a variant that integrates with [either](basics/error-handling.md#boundary-break-for-eithers) +`OxApp` has also a variant that integrates with [either](../basics/error-handling.md#boundary-break-for-eithers) blocks for direct-style error handling called `OxApp.WithEitherErrors[E]`. Here, `E` is the type of errors from the `run` function that you want to handle. The interesting bit is that `run` function in `OxApp.WithEitherErrors` receives an `either` block token of type `EitherError[E]` (which itself is an alias for `Label[Either[E, ExitCode]]` as `either` diff --git a/generated-doc/out/repeat.md b/generated-doc/out/utils/repeat.md similarity index 96% rename from generated-doc/out/repeat.md rename to generated-doc/out/utils/repeat.md index e49b5fb3..2ed4843e 100644 --- a/generated-doc/out/repeat.md +++ b/generated-doc/out/utils/repeat.md @@ -20,7 +20,7 @@ The `repeat` API uses `scheduled` underneath with DSL focused on repeats. See [s Similarly to the `retry` API, the `operation` can be defined: * directly using a by-name parameter, i.e. `f: => T` * using a by-name `Either[E, T]` -* or using an arbitrary [error mode](basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. +* or using an arbitrary [error mode](../basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. ## Configuration diff --git a/generated-doc/out/resources.md b/generated-doc/out/utils/resources.md similarity index 100% rename from generated-doc/out/resources.md rename to generated-doc/out/utils/resources.md diff --git a/generated-doc/out/retries.md b/generated-doc/out/utils/retries.md similarity index 97% rename from generated-doc/out/retries.md rename to generated-doc/out/utils/retries.md index d242f000..e22dcd0c 100644 --- a/generated-doc/out/retries.md +++ b/generated-doc/out/utils/retries.md @@ -20,7 +20,7 @@ The `retry` API uses `scheduled` underneath with DSL focused on retries. See [sc The `operation` can be provided directly using a by-name parameter, i.e. `f: => T`. There's also a `retryEither` variant which accepts a by-name `Either[E, T]`, i.e. `f: => Either[E, T]`, as well as one -which accepts arbitrary [error modes](basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. +which accepts arbitrary [error modes](../basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. ## Configuration diff --git a/generated-doc/out/scheduled.md b/generated-doc/out/utils/scheduled.md similarity index 97% rename from generated-doc/out/scheduled.md rename to generated-doc/out/utils/scheduled.md index 6454e02c..e014fb0e 100644 --- a/generated-doc/out/scheduled.md +++ b/generated-doc/out/utils/scheduled.md @@ -9,7 +9,7 @@ In fact `retry` and `repeat` use `scheduled` internally. Similarly to the `retry` and `repeat` APIs, the `operation` can be defined: * directly using a by-name parameter, i.e. `f: => T` * using a by-name `Either[E, T]` -* or using an arbitrary [error mode](basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. +* or using an arbitrary [error mode](../basics/error-handling.md), accepting the computation in an `F` context: `f: => F[T]`. ## Configuration diff --git a/generated-doc/out/utility.md b/generated-doc/out/utils/utility.md similarity index 100% rename from generated-doc/out/utility.md rename to generated-doc/out/utils/utility.md