Mirror: 🎩 A tiny but capable push & pull stream library for TypeScript and Flow

Add misc pages to documentation (#30)

* Add introduction content

* Add "Getting Started" page

* Add initial basics section

* Add index content for Basics

* Fix comment syntax in architecture

* Add publish and forEach to sinks doc

* Add toPromise to sinks doc

* Add fromPromise to sources doc

* Apply suggestions and rewrite some sections

Co-authored-by: Parker Ziegler <parkerziegler@users.noreply.github.com>

+1 -1
docs/api/index.md
···
---
title: API Reference
-
order: 1
+
order: 3
---
Wonka, in essence, can be used to create sources, to transform sources with operators,
+103 -12
docs/api/sinks.md
···
`subscribe` accepts a callback function to execute when data is received from the source, in addition to the source itself.
```reason
-
open Wonka;
-
open Wonka_sources;
-
open Wonka_sinks;
-
-
let source = fromArray([|1, 2, 3|]);
-
source |> subscribe((. _val) => print_int(_val));
+
Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 123 to the console. */
```
```typescript
import { pipe, fromArray, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3]);
+
pipe(
+
fromArray([1, 2, 3]),
+
subscribe((x) => console.log(x))
+
); // Prints 123 to the console.
+
```
+
+
`subscribe` also returns a "subscription" type, which can be used to
+
unsubscribe from the source. This allows you to cancel a source and stop receiving
+
new incoming values.
+
+
```reason
+
let subscription = source
+
|> Wonka.subscribe((. x) => print_int(x));
+
+
subscription.unsubscribe();
+
```
+
+
```typescript
+
import { pipe, subscribe } from 'wonka';
+
+
const [unsubscribe] = pipe(
+
source,
+
subscribe((x) => console.log(x));
+
);
+
+
unsubscribe();
+
```
+
+
## forEach
+
+
`forEach` works the same as `subscribe` but doesn't return a subscription.
+
It will just call the passed callback for each incoming value.
+
+
```reason
+
Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.forEach((. x) => print_int(x));
+
/* Returns unit; Prints 123 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, forEach } from 'wonka';
+
+
pipe(
+
fromArray([1, 2, 3]),
+
forEach((x) => console.log(x))
+
); // Returns undefined; Prints 123 to the console.
+
```
+
+
## publish
+
+
`publish` subscribes to a source, like `subscribe` does, but doesn't accept
+
a callback function. It's useful for side-effects, where the values are already being
+
used as part of the stream itself.
+
+
In this example we're using [`onPush`](./operators.md#onpush) to pass a callback to react to incoming
+
values instead.
+
+
```reason
+
Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.onPush((. x) => print_int(x))
+
|> Wonka.publish;
+
/* Prints 123 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, onPush, publish } from 'wonka';
pipe(
-
source,
-
subscribe((x) => {
-
console.log(x);
-
});
+
fromArray([1, 2, 3]),
+
onPush((x) => console.log(x)),
+
publish
+
); // Prints 123 to the console.
+
```
+
+
## toPromise
+
+
`toPromise` returns a promise, which resolves on the last value of a source.
+
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.toPromise
+
|> Js.Promise.then_(x => {
+
print_int(x);
+
Js.Promise.resolve(())
+
})
+
/* Prints 3 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, toPromise } from 'wonka';
+
+
const promise = pipe(
+
fromArray([1, 2, 3]),
+
toPromise,
);
-
// Prints 123 to the console.
+
+
promise.then(x => console.log(x));
+
// Prints 3 to the console.
```
+
+
If you have a source that doesn't complete and are looking to resolve on the first
+
value instead of the last, you may have to apply `take(1)` to your source.
+28 -1
docs/api/sources.md
···
`fromDomEvent` will turn a DOM event into a Wonka source, emitting the DOM events
on the source whenever the DOM emits them on the passed element.
-
> This source will only work in a JavaScript environment, and will be excluded
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
> when compiling natively.
```reason
···
fromDomEvent(element, 'click'),
subscribe(e => console.log(e))
);
+
```
+
+
## fromPromise
+
+
`fromPromise` transforms a promise into a source, emitting the promisified value on
+
the source once it resolves.
+
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
let promise = Js.Promise.make(1); /* Just an example promise */
+
+
Wonka.fromPromise(promise)
+
|> Wonka.subscribe((. x) => Js.log(x));
+
/* Prints 1 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromPromise, subscribe } from 'wonka';
+
+
const promise = Promise.resolve(1); // Just an example promise
+
+
pipe(
+
fromPromise(promise),
+
subscribe(e => console.log(e))
+
); // Prints 1 to the console.
```
## empty
+129
docs/basics/architecture.md
···
+
---
+
title: Architecture
+
order: 1
+
---
+
+
It may be useful to understand how Wonka's sources work internally
+
if you want to write a new operator from scratch or contribute to it.
+
+
This section explains how Wonka works internally and how it differs from
+
the callbag specification.
+
+
## Just Functions
+
+
Internally Wonka only uses functions with rather simple signatures to
+
make its streams work.
+
+
We have sinks on one end, which need to receive values, and sources
+
on the other, which need to send values.
+
The sink is therefore just a function that we call with values over time.
+
This is called a "push" signal.
+
+
Because a sink has a start, incoming values, and an end, there are three
+
signals that a sink can receive: `Start`, `Push`, and `End`.
+
+
``` reason
+
type signalT('a) =
+
| Start
+
| Push('a)
+
| End;
+
+
type sinkT('a) = (. signalT('a)) => unit;
+
```
+
+
As shown, the sink is just a function accepting a signal as its argument.
+
+
When the stream starts then the sink is called with `Start`,
+
Then for every incoming, new value it's called with `Push('a)`,
+
and when the stream ends it's finally called with `End`.
+
+
Since we want a source to send these values to the sink, the source is
+
also just a function and it accepts a sink as its argument.
+
+
``` reason
+
type sourceT('a) = sinkT('a) => unit;
+
```
+
+
This is completely sufficient to represent simple "push" streams, where
+
values are pushed from the source to the sink. They essentially flow from
+
the "top" to the "bottom".
+
+
Operators are just functions that transform a source. They take a
+
source and some number of arguments and return a new source.
+
Internally they may also create a new sink function that wraps the
+
sink that their source will be called with.
+
+
The type signature of an operator with no other arguments is thus:
+
+
``` reason
+
type operatorT('a, 'b) = sourceT('a) => sourceT('b);
+
/* which is the same as: */
+
type operatorT('a, 'b) = (sourceT('a), sinkT('b)) => unit;
+
```
+
+
## Adding Callbacks
+
+
To complete this pattern we're still missing a single piece: callbacks!
+
+
Previously, we've looked at how sources are functions that accept sinks, which
+
in turn are functions accepting a signal. What we're now missing is what makes
+
Wonka's streams also work as iterables.
+
+
We'd also like to be able to _cancel_ streams, so that we can interrupt
+
them and not receive any more values.
+
+
We can achieve this by passing a callback function on when a stream starts.
+
In Wonka, a sink's `Start` signal also carries a callback that is used to communicate
+
back to the source, making these "talkback signals" flow from the bottom to the top.
+
+
``` reason
+
type talkbackT =
+
| Pull
+
| Close;
+
+
type signalT('a) =
+
| Start((. talkbackT) => unit)
+
| Push('a)
+
| End;
+
```
+
+
This is like the previous `signalT('a)` definition, but the `Start` signal has the
+
callback definition now. The callback accepts one of two signals: `Pull` or `Close`.
+
+
`Close` is a signal that will cancel the stream. It tells the source to stop sending
+
new values.
+
+
The `Pull` signal is a signal that asks the source to send the next value. This is
+
especially useful to represent iterables. In practice a user would never send this
+
signal explicitly, but sinks would send the signal automatically after receiving the
+
previous value from the stream.
+
+
In asynchronous streams the `Pull` signal is of course a no-op. It won't do
+
anything since we can't ask for asynchronous values.
+
+
## Comparison to Callbags
+
+
This is the full pattern of Wonka's streams and it's a little different from callbags.
+
These changes have been made to make Wonka's streams typesafe. But there's
+
also a small omission that makes Wonka's streams easier to explain.
+
+
In Callbags, sources don't just accept sinks as their only argument. In fact, in
+
callbags the source would also receive three different signals. This can be useful
+
to represent "subjects".
+
+
A subject is a sink and source combined. It can be used to dispatch values imperatively,
+
like an event dispatcher.
+
+
In Wonka there's a separate type for subjects however, since this reduces the
+
complexity of its streams a lot:
+
+
``` reason
+
type subjectT('a) = {
+
source: sourceT('a),
+
next: 'a => unit,
+
complete: unit => unit,
+
};
+
```
+
+
Hence in Wonka a subject is simply a wrapper around a source and a `next` and `complete`
+
method.
+66
docs/basics/background.md
···
+
---
+
title: Background
+
order: 0
+
---
+
+
In a lot of daily tasks in programming we come across patterns where
+
we deal with lists of values. In JavaScript we'd reach to arrays to
+
collect them, and luckily there are plenty of methods built-in
+
to modify such an array, such as `map`, `filter` and `reduce`.
+
+
Things become more complex when we're dealing with lists that
+
are infinite. In such a case we may reach to iterables. We could
+
expect an iterable that continuously outputs numbers, counting up
+
infinitely, or rather until it reaches the maximum integer.
+
+
When we're dealing with asynchronous lists of values things also
+
become more complex. We're often confronted with event streams,
+
where events or even regular values come in over time.
+
+
In either case what we're dealing with are essentially [immutable,
+
asynchronous iterables](https://medium.com/@andrestaltz/2-minute-introduction-to-rx-24c8ca793877).
+
+
Wonka is a library to provide a primitive to solve these problems and
+
is both an iterable programming library _and_ a reactive stream programming
+
library.
+
+
It can be compared to observables and iterables in one library, but is
+
based on and essentially a ["callbag" library](https://staltz.com/why-we-need-callbags.html).
+
+
## Sources, Operators, and Sinks
+
+
When we're thinking of solving problems with streams, it's always
+
a good idea to look at how we're solving problems with arrays.
+
+
Since Wonka's streams are an entirely new primitive, Wonka has to provide
+
all utilities that you as a developer may need to work with them.
+
Specifically we have to make sure that it's easy to _create_, _transform_,
+
and _consume_ these streams.
+
+
If we compare these utilities to arrays, _creating_ an array is similar to
+
creating a stream. So Wonka has utilities such as [`fromArray`](../api/sources.md#fromArray) to
+
create a new source.
+
+
A **source** is what we call a stream in Wonka. This is because it
+
doesn't strictly follow the definition or specification of observables nor
+
iterables. So we're calling them **sources** since they're just a **source**
+
of values over time.
+
+
Next we would like to _transform_ sources to make them useful.
+
Like with arrays we may want to map, filter, and reduce them,
+
so Wonka has **operators** like [`filter`](../api/operators.md#filter) and [`map`](../api/operators.md#map).
+
But since Wonka is like a toolkit, it comes with a lot more utilities than
+
just that.
+
+
In general, **operators** will accept some arguments and a source
+
and output a new, transformed source.
+
+
Lastly, the sources we create wouldn't be of much use if we weren't
+
able to _consume_ them. This is similar to using `forEach` on an
+
array to iterate over its values. Wonka has a [`subscribe`](../api/sinks.md#subscribe) function which
+
works similarly to how an observable's subscribe method may work.
+
This is because Wonka's sources are entirely cancellable.
+
+
To summarise, Wonka's streams are _sources_ of values, which
+
can be transformed using _operators_, which create new _sources_.
+
If we want to consume a _source_ we use a _sink_.
+12
docs/basics/index.md
···
+
---
+
title: Basics
+
order: 2
+
---
+
+
Wonka introduces a new primitive for streams.
+
This part of the documentation explains both the motivation
+
behind creating and using a new stream primitive and how these
+
work internally in Wonka.
+
+
- [Background](./background.md) — learn what streams are
+
- [Architecture](./architecture.md) — learn how Wonka's streams work internally
+119
docs/getting-started.md
···
+
---
+
title: Getting Started
+
order: 1
+
---
+
+
This page will explain how to install the Wonka package and
+
its basic usage and helper functions.
+
+
## Installation
+
+
The `wonka` package from `npm` is all you need to install to use
+
Wonka. The process is the same with `yarn` and `esy`.
+
+
```bash
+
yarn add wonka
+
# or with npm:
+
npm install --save wonka
+
# or with esy:
+
esy add wonka
+
```
+
+
For **JavaScript projects**, the package contains both CommonJS and
+
ES Modules bundles. For Flow and TypeScript the package also contains
+
typings files already, so if you're using either you're already done and
+
ready to go.
+
+
If you're using **BuckleScript** or `bs-native` you will need to add `"wonka"`
+
to your `bs-dependencies` in your `bsconfig.json` configuration file:
+
+
```diff
+
{
+
"name": "<some_name>",
+
"version": "0.1.0",
+
"sources": ["src"],
+
"bsc-flags": ["-bs-super-errors"],
+
"bs-dependencies": [
+
+ "wonka"
+
]
+
}
+
```
+
+
If you're using **Dune** and **Esy** you will need to add `wonka` to
+
your `libraries` entry in the respective `dune` configuration file:
+
+
```diff
+
(library
+
(name some_name)
+
(public_name some_name)
+
+ (libraries wonka)
+
)
+
```
+
+
## Usage with JavaScript
+
+
In most cases you'll simply import or require `wonka` and use its exposed
+
methods and utilities. In both CommonJS and ES Modules the Wonka package
+
simply exposes all its utilities.
+
+
```js
+
// With CommonJS
+
const { fromArray } = require('wonka');
+
// With ES Modules
+
import { fromArray } from 'wonka';
+
```
+
+
There are also some special operators in Wonka that will only be exposed in
+
Web/JavaScript environments, like `fromPromise`, `toPromise`,
+
or `fromEvent`, or even `debounce` and `throttle`.
+
In TypeScript and Flow the typings also expose all types.
+
+
There's also a special utility in JavaScript environments to replace the pipeline
+
operator. This function is called `pipe` and simply calls functions that it's
+
being passed in order with the previous return value.
+
+
```js
+
import { pipe } from 'wonka';
+
+
const output = pipe(
+
'test',
+
x => x + ' this',
+
x => x.toUpperCase()
+
);
+
+
output; // "TEST THIS"
+
```
+
+
As shown above, the `pipe` function takes the first argument and passes it
+
in order to the other function arguments. The return value of one function will
+
be passed on to the next function.
+
+
In TypeScript and Flow the `pipe` function is also typed to handle all generics
+
in Wonka utilities correctly. Using it will ensure that most of the time you won't
+
have to specify the types of any generics manually.
+
+
If you're using Babel and the [pipeline proposal plugin](https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator), you can just use
+
the pipeline operator to do the same and not use the `pipe` helper.
+
+
## Usage with Reason
+
+
Everything in the Wonka package is exposed under a single module called `Wonka`.
+
This module also contains `Wonka.Types`, which contains all internal types of the Wonka
+
library, but you will typically not need it.
+
+
In `BuckleScript` when you're compiling to JavaScript you will also have access to
+
more utilities like `fromPromise`, `toPromise`, `fromEvent`, or even `debounce` and `throttle`.
+
These utilities are missing in native compilation, like Dune or `bsb-native`, since they're
+
relying on JavaScript APIs like Promises, `window.addEventListener`, and `setTimeout`.
+
+
When using Wonka you'd simply either open the module and use its utilities or just
+
access them from the `Wonka` module:
+
+
```reason
+
Wonka.fromValue("test")
+
|> Wonka.map((.x) => x ++ " this")
+
|> Wonka.forEach((.x) => print_endline(x));
+
```
+
+
It's worth noting that most callbacks in Wonka need to be explicitly uncurried, since
+
this will help them compile cleanly to JavaScript.
+46 -1
docs/index.md
···
order: 0
---
-
This is Wonka!
+
Wonka is a lightweight iterable and observable library loosely based on
+
the [callbag spec](https://github.com/callbag/callbag). It exposes a set of helpers to create streams,
+
which are sources of multiple values, which allow you to create, transform
+
and consume event streams or iterable sets of data.
+
+
## What it is
+
+
Wonka is a library for streams _and_ iterables that behaves predictably
+
and can be used for many problems where you're dealing with streams of
+
values, asynchronous or not.
+
+
It's similar to [RxJS](https://github.com/ReactiveX/rxjs) in that it enables asynchronous programming with
+
observable streams, with an API that looks like functional programming on
+
iterables, but it's also similar to [IxJS](https://github.com/ReactiveX/IxJS) since Wonka streams will run
+
synchronously if an iterable source runs synchronously.
+
+
It also comes with many operators that users from [RxJS](https://github.com/ReactiveX/rxjs) will be used to.
+
+
## Compatibility
+
+
Wonka is written in [Reason](https://reasonml.github.io/), a dialect of OCaml, and can hence be used
+
for native applications. It is also compiled using [BuckleScript](https://bucklescript.github.io) to plain
+
JavaScript and has typings for [TypeScript](https://www.typescriptlang.org/) and [Flow](https://flow.org/).
+
+
This means that out of the box Wonka is usable in any project that use the following:
+
+
- Plain JavaScript
+
- TypeScript
+
- Flow
+
- Reason/OCaml with BuckleScript
+
- Reason/OCaml with `bs-native`
+
- Reason/OCaml with Dune and Esy
+
+
In summary, Wonka provides a consistent interface in and works across
+
TypeScript/Flow/Reason/OCaml environments with full type safety.
+
+
## About the docs
+
+
As mentioned in the prior section, Wonka supports not one but a couple of
+
environments and languages. To accommodate for this, most of the docs
+
are written with examples and sections for TypeScript and Reason.
+
+
We don't provide examples in most parts of the docs for Flow and OCaml because
+
their respective usage is almost identical to TypeScript and Reason, so for
+
the most part the examples mostly deal with the differences between a
+
TypeScript and a Reason project.