Skip to content

Commit

Permalink
docs: improve documentation aligning to last version + fix some typo
Browse files Browse the repository at this point in the history
  • Loading branch information
tassiluca committed Mar 21, 2024
1 parent 84b9715 commit adf1a76
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.tassiLuca.dse.boundaries
import scala.util.{Failure, Success, Try, boundary}
import scala.util.boundary.{Label, break}

/** A capability enabling to break the computation returning a [[Left]] with an useful string-encoded message. */
type CanFail = Label[Left[String, Nothing]]

/** Represents a computation that will hopefully return a [[Right]] value, but might fail with a [[Left]] one. */
Expand Down
5 changes: 5 additions & 0 deletions docs/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
{
"language": "markdown",
"scheme": "file"
},
{
"language": "latex",
"scheme": "file",
"pattern": "presentation/direct-style-presentation.tex"
}
],
"files.watcherExclude": {
Expand Down
12 changes: 6 additions & 6 deletions docs/presentation/direct-style-presentation.tex
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ \section{Context}
Boundary \& break is a mechanism providing a cleaner alternative to non-local returns:
\texttt{boundary} enrich the scope with a \texttt{Label[T]} allowing to \texttt{break} the computation and return a value of type \texttt{T}.
\lstinputlisting[language=scala]{listings/intro/BoundaryExamples.scala}
Leveraging boundary \& break it's possible to implement data types to handle errors "directly" with a short exit path.
By leveraging boundary \& break it is possible to implement data types to handle errors "directly" with a short exit path.
\end{frame}
%
\begin{frame}
Expand All @@ -70,7 +70,7 @@ \section{Context}
\column{0.8\textwidth}
\lstinputlisting[language=scala]{listings/intro/ShowcasingEither.scala}
\column{0.2\textwidth}
\texttt{aggregate} returns the list of HTTP body responses, or the first encountered error.
\texttt{aggregate} returns the list of HTTP body responses or the first encountered error.
\end{columns}
\pause
\begin{columns}
Expand Down Expand Up @@ -111,7 +111,7 @@ \section{Context}
\addtolength{\leftmargini}{\labelsep}
\begin{itemize}
\item This mechanism works only for \textit{checked} exceptions and needs to be enabled with the \texttt{saferExceptions} import.
\item Unchecked exceptions can still be throw without the capability.
\item Unchecked exceptions can still be thrown without the capability.
\item If the capability is not provided: compilation error!
\end{itemize}
\begin{quote}
Expand Down Expand Up @@ -230,8 +230,8 @@ \section{Scala \texttt{gears}}
Structured concurrency guarantees that \textbf{when a group terminates all its dangling children are canceled}.
\end{block}
\begin{itemize}
\item \texttt{Async.blocking}, \texttt{Async.group} and \texttt{Future} creates a new completion group;
\item to make sure children computations are not canceled we need to \texttt{await} them.
\item \texttt{Async.blocking}, \texttt{Async.group} and \texttt{Future} create a new completion group;
\item to make sure children's computations are not canceled we need to \texttt{await} them.
\end{itemize}
\column{0.45\textwidth}
\begin{figure}
Expand Down Expand Up @@ -410,7 +410,7 @@ \section{Scala \texttt{gears}}
\item[5)] iterate over all the repositories sent over the channel as soon as they are retrieved by the service;
\item[6)] we start the analysis in a separate \texttt{Future}: the analysis is spawned as soon as a repository is fetched by the channel, preventing starting the analysis of the next repository only when the previous one is finished;
\item[9)] since the analysis are started concurrently the \texttt{updateResult} must be called safely;
\item[11)] we wait for the completion of all the started Futures.
\item[11)] wait for the completion of all the started Futures.
\end{enumerate}
\end{frame}
%
Expand Down
7 changes: 5 additions & 2 deletions docs/site/content/docs/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
In the realm of asynchronous programming, the Scala ecosystem offers a set of solid and widely adopted monadic constructs and libraries to tackle complex tasks functionally with elegance and efficiency, like [Monix Tasks](https://monix.io/docs/current/eval/task.html) and [Cats Effecs](https://typelevel.org/cats-effect/), enabling a wide range of interesting and useful features, like composable error handling, cancellation mechanisms and structured concurrency that the standard library lacks.
However, they also come with a cost: the pervasiveness of the `flatMap` operator to compose values makes the code harder to reason about and difficult and awkward to integrate with regular control structures.

In the last years, we have been assisting the increase in adoption of continuation and coroutines in modern runtimes, either exploiting some kind of fibers support, like the project Loom with Virtual Threads, or via code generation, like Kotlin Coroutines, aiming to capture the essence of effects more cleanly compared to monads.
In the last years, we have been assisting the increase in adoption of continuations and coroutines in modern runtimes, either exploiting some kind of fiber support, like the project Loom with Virtual Threads, or via code generation, like [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html).
Even Scala is not immune to this trend and a new strawman library, [Gears](https://github.com/lampepfl/gears), is currently being developed, aiming to bring direct style support for asynchronous programming.
Despite the interest and the potential this new library could bring, it is just a speck that fits into a bigger picture, which is the management of effects: the ongoing research activity led by M. Odersky has indeed the goal to, instead of pushing effect management into external libraries, upgrade the type system to handle effects natively using capabilities, as research-oriented programming languages do with Algebraic Effects (like Koka).

## Goals

The goal of this project is to explore, mainly focusing on Scala, the direct style, developing a few examples (not too complex) leveraging the new strawman library [Gears](https://github.com/lampepfl/gears), comparing it with [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) and the current implementation of monadic Futures, seeking to analyze aspects such as:
The goal of this project is to explore, mainly focusing on Scala, the direct style, developing a few examples (not too complex) leveraging the new strawman library Gears for asynchronous programming, comparing it with Kotlin Coroutines and the current implementation of monadic Futures, seeking to analyze aspects such as:

- ergonomics of the two styles;
- which of the two approaches has a real advantage in adoption;
Expand Down Expand Up @@ -66,6 +68,7 @@ In conclusion, Scala Gears is a promising project that could bring direct-style
- [Scala 3: What Is "Direct Style" by D. Wampler](https://medium.com/scala-3/scala-3-what-is-direct-style-d9c1bcb1f810#:~:text=Dean%20Wampler-,Scala%203,without%20the%20boilerplate%20of%20monads.)
- [Kotlin Coroutines documentation](https://kotlinlang.org/docs/coroutines-overview.html)
- [Pre-SIP: Suspended functions and continuations in Scala 3](https://contributors.scala-lang.org/t/pre-sip-suspended-functions-and-continuations/5801/20?u=adamw)
- [Martin Odersky - Simply Scala](https://www.youtube.com/watch?v=-qf8yteuxPs)
- [The Great Concurrency Smackdown: ZIO versus JDK by John A. De Goes](https://www.youtube.com/watch?v=9I2xoQVzrhs)
- [Continuaton, coroutine, and generator by A. Ber](https://medium.com/geekculture/continuation-coroutine-continuation-generator-9a1af03a3bed)
- [KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov](https://www.youtube.com/watch?v=_hfBv0a09Jc)
Expand Down
55 changes: 46 additions & 9 deletions docs/site/content/docs/02-boundaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,20 @@ object optional:
### `Either` + `?`

```scala
/** Represents a computation that will hopefully return a [[Right]] value but might fail with a [[Left]] one.*/
/** A capability enabling to break the computation returning a
* [[Left]] with an useful string-encoded message. */
type CanFail = Label[Left[String, Nothing]]

/** Represents a computation that will hopefully return a [[Right]] value,
* but might fail with a [[Left]] one. */
object either:

/** Defines the boundary for the [[Either]] returning computation, whose [[body]] is given in input. */
inline def apply[L, R](inline body: Label[Left[L, Nothing]] ?=> R): Either[L, R] =
boundary(Right(body))

/** Quickly break to the enclosing boundary with a [[Left]] filled with [[l]]. */
inline def left[L, R](l: L)(using Label[Left[L, Nothing]]): Either[L, R] = break(Left(l))
inline def fail[L, R](l: L)(using Label[Left[L, R]]): R = break(Left(l))

extension [L, R](e: Either[L, R])
/** @return this [[Right]] value or break to the enclosing boundary with the [[Left]] value. */
Expand All @@ -84,13 +89,12 @@ object either:
case Left(value) => break(Left(value))

extension [R](t: Try[R])
/** @return this [[Success]] value or break to the enclosing boundary with a [[Left]] containing
* the converted `Throwable` exception performed by the implicit [[converter]].
/** @return this [[Success]] value or break to the enclosing boundary with a [[Left]]
* containing the converted `Throwable` exception performed by the implicit [[converter]].
*/
inline def ?[L](using Label[Left[L, Nothing]])(using converter: Conversion[Throwable, L]): R =
t match
case Success(value) => value
case Failure(exception) => break(Left(converter(exception)))
inline def ?[L](using Label[Left[L, Nothing]])(using converter: Conversion[Throwable, L]): R = t match
case Success(value) => value
case Failure(exception) => break(Left(converter(exception)))

/** An object encapsulating a collection of `Throwable` given converters. */
object EitherConversions:
Expand All @@ -99,7 +103,40 @@ object EitherConversions:
given Conversion[Throwable, String] = _.getMessage
```

This kind of data type will be particularly useful in the next examples to quickly break in case of failures, returning the caller a meaningful error message, and simplifying the error-handling code.
This kind of data type is particularly useful to quickly break in case of failures, returning the caller a meaningful error message, and simplifying the error-handling code.

For instance, `aggregate` returns the list of HTTP body responses, or the first encountered error.

```scala
def aggregate(xs: List[Uri]): Either[String, List[String]] =
either: // boundary
xs.map(doRequest(_).?) // `?` break if doRequest returns a Left

def doRequest(endpoint: Uri): Either[String, String] =
HttpClientSyncBackend().send(basicRequest.get(endpoint)).body
```

Functions requiring the label capability can promptly break the computation upon encountering an error.
Calling side the label is defined using `either` boundary.

```scala
def getUser(id: UserId)(using CanFail): User =
val user = userBy(id)
if verifyUser(user) then user else fail("Incorrect user")
// fail is a shorthand for `break(Left("Incorrect user"))`

def getPayment(user: User)(using CanFail): PaymentMethod =
paymentMethodOf(user) match
case Some(a) if verifyMethod(a) => a
case Some(_) => fail("The payment method is not valid")
case _ => fail("Missing payment method")

def paymentData(id: UserId) = either:
val user = getUser(id)
val address = getPayment(user)
(user, address)
```


{{< button relref="/01-overview" >}} **Overview** {{< /button >}}

Expand Down
Loading

0 comments on commit adf1a76

Please sign in to comment.