From 86bbce23463865b9fd61297962a06464b0b9c8df Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 15 Dec 2023 17:41:44 -0600 Subject: [PATCH 1/3] Add Rosetta Stone Guide --- www/docs/actions.mdx | 2 +- www/docs/async-rosetta-stone.mdx | 185 +++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) diff --git a/www/docs/actions.mdx b/www/docs/actions.mdx index 17c7c750..8db2dd56 100644 --- a/www/docs/actions.mdx +++ b/www/docs/actions.mdx @@ -113,7 +113,7 @@ async function fetch(url) { } ``` -Consulting the [Async Rosetta Stone](), we can substitute the async +Consulting the [Async Rosetta Stone](async-rosetta-stone), we can substitute the async constructs for their Effection counterparts to arrive at a line for line translation. diff --git a/www/docs/async-rosetta-stone.mdx b/www/docs/async-rosetta-stone.mdx index 02e00834..c1844543 100644 --- a/www/docs/async-rosetta-stone.mdx +++ b/www/docs/async-rosetta-stone.mdx @@ -1,3 +1,12 @@ +When we say "Effection is Structured Concurrency and Effects for +JavaScript", we mean "JavaScript" seriously. You shouldn't have to +learn an entirely new way of programming just to achieve structured +concurrency. That's why the Effection APIs mirror ordinary JavaScript +APIs so closely. That way, if you know how to do it in JavaScript, you +know how to do it in Effection. + +The congruence between vanilla JavaScript constructs and their Effection +counterparts is reflected in the “Async Rosetta Stone.” | Async/Await | Effection | @@ -10,3 +19,179 @@ | `AsyncIterator` | `Subscription` | | `for await` | `for yield* each` | +## `Promise` \<=> `Operation` + +The `Promise` type serves roughly the same purpose as the `Operation`. It is a +abstract value that you can use to pause a computation, and resume when the +value has been computed. + +To use a promise: + +```js +let promise = await promise; +``` + +To use an operation: + +```js +let operation = yield* operation; +``` + +To convert from a promise to an operation, use [`call()`][call] + +```js +let operation = call(promise); +``` + +to convert from an operation to a promise, use [`run()`][run] or [`Scope.run`][scope-run] + +```js +let promise = run(operation); +``` + +## `new Promise()` \<=> `action()` + +Construct a reference to a computation that can be resolved with a callback. +In the case of `Promise()` the value will resolve in the next tick of the run +loop. + +Create a promise that resolves in ten seconds: + +```js +new Promise((resolve) => { setTimeout(resolve, 10000) }); +``` + +Create an Operation that resolves in ten seconds: + +```js +action(function*(resolve) { setTimeout(resolve, 10000) }); +``` + +A key difference is that the promise body will be executing immediately, but the +action body is only executed when the action is evaluated. Also, it is executed +anew every time the action is evaluated. + +## `await` \<=> `yield*` + +Pause a computation and resume it when the value represented by the right hand +side becomes available. + +Continue once a promise has settled: + +```javascript +await promise; +``` + +Continue when operation is complete. + +```js +yield* operation; +``` + +## `async function` \<=> `function*` + +Compose a set of computations together with logic defined by JavaScript syntax: + +Count down from 5 to 1 with an async function: + +```js +async function countdown() { + for (let i = 5; i > 1; i--) { + console.log(`${i}`); + await sleep(1000); + } + console.log('blastoff!'); +} +``` + +Count down from 5 to 1 with a generator function: + +```js +function* countdown() { + for (let i = 5; i > 1; i--) { + console.log(`${i}`); + yield* sleep(1000); + } + console.log('blastoff!'); +} +``` + +Both will print: + +``` +5 +4 +3 +2 +1 +blastoff! +``` + +To call an async function within an operation use [`call()`][call]: + +```js +yield* call(async function() { + return "hello world"; +}); +``` + +To run an operation from an async function use [`run()`][run] or [`Scope.run`][scope-run]: + +```js +await run(function*() { + return "hello world"; +}); +``` + +## `AsyncIterable` \<=> `Stream` + +A recipe for instantiating a sequence of items that can arrive over time. It is not +the sequence itself, just how to create it. + +Use an `AsyncIterable` to create an `AsyncIterator`: + +```js +let iterator = asyncIterable[Symbol.asyncIterator](); +``` + +Use a `Stream` as an operation to create a `Subscription`: + +```js +let subscription = yield* stream; +``` + +To convert an `AsyncIterable` to a `Stream` use [`stream()`][stream] + +```js +let itemStream = stream(asyncIterable); +``` + +## `AsyncIterator` \<=> `Subscription` + +A stateful sequence of items that can be evaluated one at a time. + +## `for await` \<=> `for yield* each` + +Loop over an AsyncIterable with `for await`: + +```js +for await (let item of iterable) { + //item logic +} +``` + +Loop over a `Stream` with `for yield* each` + +```js +for (let item of yield* each(stream)) { + // item logic + yield* each.next(); +} +``` + +See the definition of [`each()`][each] for more detail. + +[call]: https://deno.land/x/effection/mod.ts?s=call +[run]: https://deno.land/x/effection/mod.ts?s=run +[scope-run]: https://deno.land/x/effection/mod.ts?s=Scope#method_run_0 +[each]: https://deno.land/x/effection/mod.ts?s=each From 917e2a88fffa9740095d056ee7c352105b18ebc7 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sat, 16 Dec 2023 09:51:52 -0500 Subject: [PATCH 2/3] Added import statements --- www/docs/async-rosetta-stone.mdx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/www/docs/async-rosetta-stone.mdx b/www/docs/async-rosetta-stone.mdx index c1844543..0685d4ae 100644 --- a/www/docs/async-rosetta-stone.mdx +++ b/www/docs/async-rosetta-stone.mdx @@ -40,12 +40,16 @@ let operation = yield* operation; To convert from a promise to an operation, use [`call()`][call] ```js +import { call } from 'effection'; + let operation = call(promise); ``` to convert from an operation to a promise, use [`run()`][run] or [`Scope.run`][scope-run] ```js +import { run } from 'effection'; + let promise = run(operation); ``` @@ -64,6 +68,8 @@ new Promise((resolve) => { setTimeout(resolve, 10000) }); Create an Operation that resolves in ten seconds: ```js +import { action } from 'effection'; + action(function*(resolve) { setTimeout(resolve, 10000) }); ``` @@ -107,6 +113,8 @@ async function countdown() { Count down from 5 to 1 with a generator function: ```js +import { sleep } from 'effection'; + function* countdown() { for (let i = 5; i > 1; i--) { console.log(`${i}`); @@ -130,6 +138,8 @@ blastoff! To call an async function within an operation use [`call()`][call]: ```js +import { call } from 'effection'; + yield* call(async function() { return "hello world"; }); @@ -138,6 +148,8 @@ yield* call(async function() { To run an operation from an async function use [`run()`][run] or [`Scope.run`][scope-run]: ```js +import { run } from 'effection'; + await run(function*() { return "hello world"; }); @@ -163,6 +175,8 @@ let subscription = yield* stream; To convert an `AsyncIterable` to a `Stream` use [`stream()`][stream] ```js +import { stream } from 'effection'; + let itemStream = stream(asyncIterable); ``` @@ -183,6 +197,8 @@ for await (let item of iterable) { Loop over a `Stream` with `for yield* each` ```js +import { each } from 'effection'; + for (let item of yield* each(stream)) { // item logic yield* each.next(); From d31123386b2f37df282d705db091a43882d12bb1 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sat, 16 Dec 2023 09:56:12 -0500 Subject: [PATCH 3/3] Change the order on Rosetta Stone page --- www/docs/async-rosetta-stone.mdx | 147 +++++++++++++++---------------- 1 file changed, 73 insertions(+), 74 deletions(-) diff --git a/www/docs/async-rosetta-stone.mdx b/www/docs/async-rosetta-stone.mdx index 0685d4ae..f19780bb 100644 --- a/www/docs/async-rosetta-stone.mdx +++ b/www/docs/async-rosetta-stone.mdx @@ -8,74 +8,15 @@ know how to do it in Effection. The congruence between vanilla JavaScript constructs and their Effection counterparts is reflected in the “Async Rosetta Stone.” - | Async/Await | Effection | | ---------------- | ----------------- | -| `Promise` | `Operation` | -| `new Promise()` | `action()` | | `await` | `yield*` | | `async function` | `function*` | +| `Promise` | `Operation` | +| `new Promise()` | `action()` | +| `for await` | `for yield* each` | | `AsyncIterable` | `Stream` | | `AsyncIterator` | `Subscription` | -| `for await` | `for yield* each` | - -## `Promise` \<=> `Operation` - -The `Promise` type serves roughly the same purpose as the `Operation`. It is a -abstract value that you can use to pause a computation, and resume when the -value has been computed. - -To use a promise: - -```js -let promise = await promise; -``` - -To use an operation: - -```js -let operation = yield* operation; -``` - -To convert from a promise to an operation, use [`call()`][call] - -```js -import { call } from 'effection'; - -let operation = call(promise); -``` - -to convert from an operation to a promise, use [`run()`][run] or [`Scope.run`][scope-run] - -```js -import { run } from 'effection'; - -let promise = run(operation); -``` - -## `new Promise()` \<=> `action()` - -Construct a reference to a computation that can be resolved with a callback. -In the case of `Promise()` the value will resolve in the next tick of the run -loop. - -Create a promise that resolves in ten seconds: - -```js -new Promise((resolve) => { setTimeout(resolve, 10000) }); -``` - -Create an Operation that resolves in ten seconds: - -```js -import { action } from 'effection'; - -action(function*(resolve) { setTimeout(resolve, 10000) }); -``` - -A key difference is that the promise body will be executing immediately, but the -action body is only executed when the action is evaluated. Also, it is executed -anew every time the action is evaluated. ## `await` \<=> `yield*` @@ -155,34 +96,63 @@ await run(function*() { }); ``` -## `AsyncIterable` \<=> `Stream` +## `Promise` \<=> `Operation` -A recipe for instantiating a sequence of items that can arrive over time. It is not -the sequence itself, just how to create it. +The `Promise` type serves roughly the same purpose as the `Operation`. It is a +abstract value that you can use to pause a computation, and resume when the +value has been computed. -Use an `AsyncIterable` to create an `AsyncIterator`: +To use a promise: ```js -let iterator = asyncIterable[Symbol.asyncIterator](); +let promise = await promise; ``` -Use a `Stream` as an operation to create a `Subscription`: +To use an operation: ```js -let subscription = yield* stream; +let operation = yield* operation; ``` -To convert an `AsyncIterable` to a `Stream` use [`stream()`][stream] +To convert from a promise to an operation, use [`call()`][call] ```js -import { stream } from 'effection'; +import { call } from 'effection'; -let itemStream = stream(asyncIterable); +let operation = call(promise); ``` -## `AsyncIterator` \<=> `Subscription` +to convert from an operation to a promise, use [`run()`][run] or [`Scope.run`][scope-run] -A stateful sequence of items that can be evaluated one at a time. +```js +import { run } from 'effection'; + +let promise = run(operation); +``` + +## `new Promise()` \<=> `action()` + +Construct a reference to a computation that can be resolved with a callback. +In the case of `Promise()` the value will resolve in the next tick of the run +loop. + +Create a promise that resolves in ten seconds: + +```js +new Promise((resolve) => { setTimeout(resolve, 10000) }); +``` + +Create an Operation that resolves in ten seconds: + +```js +import { action } from 'effection'; + +action(function*(resolve) { setTimeout(resolve, 10000) }); +``` + +A key difference is that the promise body will be executing immediately, but the +action body is only executed when the action is evaluated. Also, it is executed +anew every time the action is evaluated. ## `for await` \<=> `for yield* each` @@ -207,6 +177,35 @@ for (let item of yield* each(stream)) { See the definition of [`each()`][each] for more detail. +## `AsyncIterable` \<=> `Stream` + +A recipe for instantiating a sequence of items that can arrive over time. It is not +the sequence itself, just how to create it. + +Use an `AsyncIterable` to create an `AsyncIterator`: + +```js +let iterator = asyncIterable[Symbol.asyncIterator](); +``` + +Use a `Stream` as an operation to create a `Subscription`: + +```js +let subscription = yield* stream; +``` + +To convert an `AsyncIterable` to a `Stream` use [`stream()`][stream] + +```js +import { stream } from 'effection'; + +let itemStream = stream(asyncIterable); +``` + +## `AsyncIterator` \<=> `Subscription` + +A stateful sequence of items that can be evaluated one at a time. + [call]: https://deno.land/x/effection/mod.ts?s=call [run]: https://deno.land/x/effection/mod.ts?s=run [scope-run]: https://deno.land/x/effection/mod.ts?s=Scope#method_run_0