Follow the link for method references. Below is an explanation of why and how.
The two main exports of this library are implementations of the Maybe Monad and the Either Monad. I wrote this library without any formal learning in category theory, so the Rust standard library was essential to guiding my implementation of these Monad patterns. While these functional programming concepts are notoriously arcane, they are much easier to learn in practice than in theory.
Option Type (Maybe Monad)
The dreaded error
undefined is not a function could be gone forever!. Sounds
great, right? But there are still many times when you need a way to represent
nothing. What happens if your function doesn't return anything? It turns out
that there is a pattern for doing with type safety.
The option type is a simple type that can exist in one of two states--having
some value, or having none (nothing). In a way, this is very similar
to having a value or having
null. However, an option carries the context of
the type it represent regardless of it being present. Instead of calling a
function expecting a
null, you receive an option of a string
Option<string>. That option may be
Some<string>, or it may be
None. This is why it's known as the Maybe Monad.
So how does this work? We wrap the state of having a value in a box. In
Let's imagine what a
Some<string> would look like for the string
This is fine for a simple box to hold our value, but how would we represent
None? What about an empty object?
Now we have a consistent object shape, so we need a way to distinguish the state
of the option type. We want to keep our option generic, so it can't be
implemented with any kind of value checking on the
value property of the
Some object. Instead, we can use another property that will be common to both
states that we call a type discriminant.
This is enough for us to implement the option type! But keeping track of the option state is tedious, and the abstraction shouldn't allow us to know the internal state. That's where the library comes in.
Generally, usage of the option type starts by wrapping a value of which we know
the type, but we also know that it may return
undefined. To do that,
we can use the
Option.from methods (one is just an alias).
;;// Some<1>;// None
Option.of and its alias only create a
Some type if the value is not
undefined. From then on, you can make use of the methods available
Option type to perform operations safely. Let's try another example
like getting the input value from an HTMLInputElement.
// document.querySelector returns an element or null;// If we are certain this exists, we can unwrap the value.// It will throw an error if the option is a `none`;// We can also have it throw a custom error messageinput = maybeInput.expect"expected an input element with name=email";// We can safely read a value if we provide a default;// We can even do a chain of dependent actions without dealing with null!;
You can browse the set of methods known as combinators, but the most important
match method accepts an object with methods for each
possible state of the option (
None) and must return the same type
for each possibility.
Convert Tasks to Observables
The Task object is similar to RxJS observables, and can be converted in just a few lines of code.
It is important to note that while the Task model maps nicely to Observables, it is missing the mechanisms to cancel its work. For this reason, there is no teardown function returned from the subscribe function passed to the Observable's constructor.
The lack of cancellation gets at the key differences between Tasks and Observables.
- Tasks model a single chain of asynchronous work that may produce a value; Observables model a stream of asynchronous values/signals.
- Tasks do not cancel since they are more like lazy promises; Observables manage a resource lifecycle including their construction, completion, & destruction.
- Tasks model type-safe errors; Observables treat errors opaquely since they are caught and therefore cannot provide type guarantees.