Folktale is a suite of libraries for generic functional programming in JavaScript that allows you to write elegant modular applications with fewer bugs, and more reuse.
|
|
|
|
This guide will cover everything you need to start using the Folktale project right away, from giving you a brief overview of the project, to installing it, to creating a simple example. Once you get the hang of things, the Folktale By Example guide should help you understanding the concepts behind the library, and mapping them to real use cases.
Folktale is a suite of libraries for allowing a particular style of functional programming in JavaScript. This style uses overtly generic abstractions to provide the maximum amount of reuse, and composition, such that large projects can be built in a manageable way by just gluing small projects together. Since the concepts underneath the library are too generic, however, one might find it difficult to see the applications of the data structures to the problems they need to solve (which the Folktale By Example guide tries to alleviate by using real world examples and use cases to motivate the concepts). However, once these concepts are understood, you open up a world of possibilities and abstractive power that is hard to find anywhere else.
The main goal of Folktale is to allow the development of robust, modular, and reusable JavaScript components easier, by providing generic primitives and powerful combinators.
Short answer is no. You absolutely don’t need to know any sort of advanced mathematics to use the Folktale libraries.
That said, most of the concepts used by the library are derived from Category Theory, Algebra, and other fields in mathematics, so the if you want to extrapolate from the common use cases, and create new abstractions, it will help you tremendously to be familiar with these branches of mathematics.
Good, let’s get down to the good parts!
Folktale uses a fairly modular structure, where each library is provided as a separate package. To manage all of them, we use NPM. If you’re already using Node, you’re all set, just skip to the next section.
If you’re not using Node, you’ll need to install it so you can grab the libraries. Don’t worry, installing Node is pretty easy:
- Go the the Node.js download page.
- If you’re on Windows, grab the
.msi
installer. If you’re on Mac, grab the.pkg
installer.
Note
If you’re on Linux, the easiest way is to grab the Linux Binaries, extract
them to some folder, and place the node
and npm
binaries on your
$PATH
~ mkdir ~/Applications/node-js
~ cd ~/Applications/node-js
~ wget http://nodejs.org/dist/v0.10.24/node-v0.10.24-linux-x64.tar.gz
# or linux-x86.tar.gz, for 32bit architectures
~ tar -xzf node-.tar.gz
~ cd /usr/local/bin
~ sudo ln -s ~/Applications/node-js/node-v0.10.24-linux-x64/bin/node node
~ sudo ln -s ~/Applications/node-js/node-v0.10.24-linux-x64/bin/npm npm
On Ubuntu, you can also use Chris Lea’s PPA.
Now that you have Node, we can get down to actually using the library. For this, let’s create a new directory where we’ll install the library:
~ mkdir ~/folktale-hello-world
~ cd ~/folktale-hello-world
~ npm install data.maybe
The npm install
command will grab the library for you. In this case, the
library is data.maybe
, which provides a data structure for modelling values
that might not exist (like nulls, but safer). It should only take a few seconds
to get everything installed, and if all goes well, you’ll have a
node_modules
folder with all the stuff.
Now, run node
to get dropped into a Read-Eval-Print-Loop, which
will allow us to play around with the library interactively. Once in the REPL,
you can load the library:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // We load the library by "require"-ing it
var Maybe = require('data.maybe')
// Returns Maybe.Just(x) if some `x` passes the predicate test
// Otherwise returns Maybe.Nothing()
function find(predicate, xs) {
return xs.reduce(function(result, x) {
return result.orElse(function() {
return predicate(x)? Maybe.Just(x)
: /* otherwise */ Maybe.Nothing()
})
}, Maybe.Nothing())
}
var numbers = [1, 2, 3, 4, 5]
var anyGreaterThan2 = find(function(a) { return a > 2 }, numbers)
// => Maybe.Just(3)
var anyGreaterThan8 = find(function(a) { return a > 8 }, numbers)
// => Maybe.Nothing
|
Running in the browser takes just a little bit more of effort. To do so, you’ll first need the Browserify too, which converts modules using the Node format, to something that the Browsers can use. Browserify is just an NPM module, so it’s easy to get it:
$ npm install browserify
Since Browserify has quite a bit more of dependencies than our
data.maybe
library, it’ll take a few seconds to fully install it. Once
you’ve got Browserify installed, you’ll want to create your module using the
Node format. So, create a hello.js
with the following content:
1 2 3 4 5 6 | // We load the data.maybe library, just like in Node
var Maybe = require('data.maybe')
Maybe.Just("Hello, world!").chain(function(value) {
document.body.appendChild(document.createTextNode(value))
})
|
To compile this file with Browserify, you run the Browserify command giving the file as input:
~ $(npm bin)/browserify hello.js > bundle.js
And finally, include the bundle.js
file in your webpage:
<!DOCTYPE html>
<html>
<head>
<title>Hello, World</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
By opening the page on your webbrowser, you should see Hello, World!
added to
the page.
Folktale is a large collection of base libraries, and still largely a work in progress, but there’s a lot that is already done and can be used today!
- Safe optional value (replaces nullable types) with the Maybe structure.
- Disjunction type (commonly encodes errors) with the Either structure.
- Disjunction with failure aggregation with the Validation structure.
- Asynchronous values and computations with the Task structure.
- Common and useful combinators from Lambda Calculus.
- Common and useful monadic combinators.
Each of them are fairly broad concepts. The recommended way of getting familiar with them is working through the Folktale By Example guide, which will explain each concept through a series of real world use cases.
Warning
This book is a work in progress!
This is largely a draft at this point, so if you see any problems, feel free to file a ticket and I’ll get to fixing it up asap.
Category Theory is a relatively new branch of mathematics with fairly abstract concepts. Functional programming libraries use such concepts for maximising their abstraction power and general usefulness, but it comes with a certain drawback: most of the constructions provide little or no guidance for concrete use cases. This is a problem in particular with newcomers to this style of programming, who often find themselves lost, asking questions like “But why are Monads useful?”
In this book, you will walk through concrete applications of concepts in Category Theory, Abstract Algebra, and other branches of mathematics used by functional programming, as well as concepts from functional programming itself. By looking at concrete instances of these concepts, you can build a mental framework for generalising and abstracting problems in a way that makes your code more reusable, and more robust.
Note
Do note that this isn’t a book about Category Theory or any other mathematical field, the concepts presented in this book are just influenced by them.
People tend to have a fairly difficult time reasoning about abstractions, but they can easily recognise concrete instances of those abstractions. With enough examples, they can then build their own mental model of that abstraction and, having that mental model, they’ll be able to apply that generalisation to find other concrete instances of that abstractions on their own.
With that in mind, this book tries to present its readers with concrete applications of a concept before discussing the concept itself. It does so by presenting, at each chapter, a set of related problems, discussing concrete solutions for those problems, and finally extrapolating to a general solution that captures the pattern in those concrete solutions.
This book is aimed at intermediate and advanced JavaScript programmers who want to take advantage of mathematical concepts to make their JavaScript code bases simpler, more robust and reusable.
You’re expected to be comfortable not only with the syntax and basic concepts of the JavaScript language, but also with concepts such as higher-order programming, first-class functions, objects, prototypes, and dynamic dispatch, which are going to be the basis for the concepts discussed in this book. Non-JavaScript programmers familiar with those concepts might be able to translate the concepts to their languages, with some work, but a different book might be better suited for their needs.
To make the most out of this book, you’ll also need some school-level mathematical reasoning skills, since the generalisation of the concepts will be presented as mathematical laws. Properly understanding them will take some knowledge of equality, substitutability and unification. Albert Y. C. Lai has described the prerequisite mathematical skills for functional programming on a web page.
The book is split into a few sections, each section starts with a description its theme, prerequisites and motivation, spends a few chapters talking about concrete examples inside that theme, and concludes with a summary of the abstractions presented. Sections build on top of each other, so it might be difficult to read the book in a non-linear way.
Section 1 discusses functions, and how composition can be used to build new
functionality from existing one easily, as well as how JavaScript function
affect composition in JS. It presents the core.lambda
, core.arity
and
core.operator
libraries.
Section 2 discusses data structures and their transformations. It talks
about concepts such as Functors and recursion schemes such as
Catamorphisms. This section also gives a general idea of how sum types are
modelled in Folktale. It presents the data.maybe
, data.either
and
data.validation
libraries.
Section 3 discusses advanced transformations of data structures. It talks about concepts such as Applicative Functors and Monads. It presents new facets of the libraries presented in Section 2.
Section 4 discusses approaches to asynchronous concurrency when dealing with
simple values. It revisits Monads, and talks about new concepts such as
Continuation-Passing style, Tasks, and Futures. It presents the
data.task
library.
Section 5 discusses advanced approaches to dealing with multi-value
concurrency. It expands on Section 4 by presenting new concepts such as
back-pressure, Signals and Channels. It presents the data.channel
and
data.signal
libraries.
Section 6 discusses data validation and normalisation in more detail. It
presents the data.validation
and core.check
libraries.
The book contains a heavy amount of examples, all of which can be found in the
Folktale GitHub repository, under the docs/examples
folder.
We are about to study the idea of a computational process. Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data. The evolution of a process is directed by a pattern of rules called a program. People create programs to direct processes. In effect, we conjure the spirits of the computer with our spells.
—G. J. Sussman, H. Abelson and J. Sussman in Structure and Interpretation of Computer Programs
In programming we solve problems by breaking them into smaller parts, and then putting such smaller parts back together somehow to construct the whole solution. The same is largely true in functional programming. We break problems down into smaller problems, solve them, and put them back together to solve the large one.
Because of this, most of the emphasis in functional programming is in composition. More concretely, functional programs try to find the right data structures to represent the problem, and then figure out how to transform such data structures from the original problem into the proposed solution. Given that transformations from the problem to the solution are usually too big, functional programs break these transformations into smaller transformations, and even smaller transformations, and then build a big transformation by using the smaller ones.
Transformations in functional programming are captured by, as one would probably have guessed, functions. And the act of putting such functions back together to form bigger things is called function composition. This section will discuss how composition helps writing better JavaScript programs, and how one can use the Folktale libraries for that.
Folktale is a suite of libraries for generic functional programming in JavaScript. It allows the construction of elegant, and robust programs, with highly reusable abstractions to keep the code base maintainable.
The library is organised by a variety of modules split into logical categories,
with the conventional naming of <Category>.<Module>
. This page provides
reference documentation for all the modules in the Folktale library, including
usage examples and cross-references for helping you find related concepts that
might map better to a particular problem.
Provides the most basic and essential building blocks and compositional operations, which are likely to be used by most programs.
core.arity
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/core.arity/issues |
Version: | 1.0.0 |
Repository: | https://github.com/folktale/core.arity |
Portability: | Portable |
npm package: | core.arity |
Restricts the arity of variadic functions.
Since all functions in JavaScript are variadic, programmers often take advantage of this fact by providing more arguments than what a function takes, and the callee can just ignore them. With curried functions, calling a binary function with three arguments ends up invoking the return value of the function with the extra argument!
var curry = require('core.lambda').curry;
function add(a, b) {
return a + b;
}
var cadd = curry(2, add);
cadd(1)(2) // => 3
cadd(1, 2) // => 3
cadd(1, 2, 4) // => Error: 3 is not a function
To fix this, one would need to wrap the curried function such that the wrapper only passes two arguments to it, and ignores the additional ones:
var binary = require('core.arity').binary;
binary(cadd)(1, 2, 4) // => 3
core.arity.
nullary
(f)¶Returns: | A function that takes no arguments. |
---|
(α₁, α₂, ..., αₙ → β) → (Unit → β)
Restricts a variadic function to a nullary one.
core.arity.
unary
(f)¶Returns: | A function that takes one argument. |
---|
(α₁, α₂, ..., αₙ → β) → (α₁ → β)
Restricts a variadic function to an unary one.
core.check
¶Stability: | 1 - Experimental |
---|---|
Bug Tracker: | https://github.com/folktale/core.check/issues |
Version: | 0.1.0 |
Repository: | https://github.com/folktale/core.check |
Portability: | Portable |
npm package: | core.check |
Interface checking for JS values.
JavaScript is an untyped language, and this makes it fairly flexible for
certain things. More often than not, however, you want to make sure that the
values going into a certain code path have some kind of structure, to reduce
the complexity of the whole program. core.check
helps you to do this by
providing composable contracts:
1 2 3 4 5 | check.assert(check.String(1))
// => Error: Expected 1 to have tag String
check.assert(check.Or([check.String, check.Boolean])(1))
// => Error: Expected 1 to have tag String, or 1 to have tag Boolean
|
core.check
can also be used for validating data structures without
crashing the process. All contracts return a Validation(Violation, α)
result. One can then use the cata()
operation on the data.validation.Validation
object to deal with the
result of the operation:
1 2 3 4 5 6 7 8 9 10 11 12 | function logString(a) {
return check.String(a).cata({
Failure: function(error){ return 'Not a string: ' + a },
Success: function(value){ return 'String: ' + value }
})
}
logString(1)
// => 'Not a string: 1'
logString('foo')
// => 'String: foo'
|
core.check.
Or
(interfaces)¶Returns: | An interface that matches any of the given interfaces. |
---|
Array(α → Validation(Violation, α)) → α → Validation(Violation, α)
core.check.
And
(interfaces)¶Returns: | An interface that matches only if all of the given interfaces match. |
---|
Array(α → Validation(Violation, α)) → α → Validation(Violation, α)
core.check.
Seq
(interfaces)¶Returns: | An interface that matches an N-Tuple with the given interfaces. |
---|
Array( α₁ → Validation<Violation, α₁)
, α₂ → Validation(Violation, α₂)
, ...
, αₙ → Validation(Violation, αₙ)>
→ Array(α₁, α₂, ..., αₙ)
→ Validation(Violation, Array(α₁, α₂, ..., αₙ))
core.check.
Null
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only null
values.
core.check.
Undefined
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only undefined
values.
core.check.
Boolean
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only Boolean
values.
core.check.
Number
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only Number
values.
core.check.
String
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only String
values.
core.check.
Function
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only Function
values.
core.check.
Array
(aValue)¶Any → Validation(Violation, Any)
An interface that matches only Array
values.
core.check.
Violation
¶type Violation = Tag(String, Any)
| Equality(Any, Any)
| Identity(Any, Any)
| Any(Array(Any))
| All(Array(Any))
implements
Equality, Extractor, Reflect, Cata, Semigroup, ToString
Represents a violation of an interface’s constraint.
Violation.prototype.
isEquality
¶@Violation => Boolean
true
is the Violation has an Equality
tag.
Violation.prototype.
cata
(aPattern)¶Returns: | The result of applying the right transformation to the Violation. |
---|
@Violation => { r | Pattern } → β
where type Pattern {
Tag: (String, Any) → β,
Equality: (Any, Any) → β,
Identity: (Any, Any) → β,
Any: Array(Any) → β,
All: Array(Any) → β
}
Provides a crude form of pattern matching over the Violation ADT. Since Violation also implements the Extractor interface, you may choose to use the Sparkler Sweet.js macro instead for a more powerful form of pattern matching.
core.inspect
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/core.inspect/issues |
Version: | 1.0.3 |
Repository: | https://github.com/folktale/core.inspect |
Portability: | Portable |
npm package: | core.inspect |
Any → String
Human-readable representations for built-in and custom objects.
Require the core.inspect
package, after installing it:
var inspect = require('core.inspect')
The module itself is a specialised form of core.inspect.show()
that
has a limited maxDepth
:
1 2 | inspect([1, [2, [3, [4, [5, [6]]]]]])
// => '[1, [2, [3, [4, [5, (...)]]]]]'
|
Some objects provide a custom representation, some do not. You usually want
to see the custom textual representation if an object has it, since just
showing its own properties might not give you enough information about it,
or might not be as easy to read. But you also want to represent objects that
don’t have a custom representation as something more useful than [object
Object]
. core.inspect
solves this problem.
Consider a simple custom type representing a point in a 2d plane:
1 2 3 4 5 6 7 8 | function Point2d(x, y) {
this.x = x;
this.y = y;
}
Point2d.prototype.toString = function() {
return 'Point2d(' + this.x + ', ' + this.y + ')'
}
|
If one wants to print a textual representation of this type, they’d call
Point2d.toString()
:
1 2 3 | var p1 = new Point2d(10, 20);
p1.toString()
// => (String) "Point2d(10, 20)"
|
But what if you don’t know if the object you’re dealing with has a custom textual representation or not? In that case, you’d usually try to just display its properties:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var Maybe = require('data.maybe');
var player = {
lastPosition: Maybe.Nothing(),
currentPosition: Maybe.Just(new Point2d(10, 20))
}
player
// => {
// "lastPosition": {},
// "currentPosition": {
// "value": {
// "x": 10,
// "y": 20
// }
// }
// }
|
In this example we have no way of knowing that lastPosition
contains a
Maybe.Nothing
value, or that currentPosition
is wrapped in a
Maybe.Just
. A more informative description would be what
core.inspect
gives you:
1 2 3 4 | var show = require('core.inspect');
show(player);
// => '{"lastPosition": Maybe.Nothing, "currentPosition": Maybe.Just(Point2d(10, 20))}'
|
core.lambda
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/core.lambda/issues |
Version: | 1.0.0 |
Repository: | https://github.com/folktale/core.lambda |
Portability: | Portable |
npm package: | core.lambda |
Core combinators and higher-order functions.
Functional programming places heavy emphasis in composition (specially
function composition), but JavaScript lacks built-in functionality for
composing and transforming functions in order to compose
them. core.lambda
fills this gap by providing tools for composing
functions, altering the shape of a function in order to compose them in
different ways, and currying/uncurrying.
identity
¶core.lambda.
identity
(a)¶Returns: | The argument it’s given. |
---|
α → α
The identity combinator. Always returns the argument it’s given.
1 2 | identity(3) // => 3
identity([1]) // => [1]
|
constant
¶core.lambda.
constant
(a, b)¶Returns: | The first argument it’s given. |
---|
α → β → α
The constant combinator. Always returns the first argument it’s given.
1 2 | constant(3)(2) // => 3
constant('foo')([1]) // => 'foo'
|
apply
¶core.lambda.
apply
(f, a)¶Returns: | The result of applying f to a . |
---|
(α → β) → α → β
Applies a function to an argument.
1 2 | var inc = function(a){ return a + 1 }
apply(inc)(3) // => 4
|
apply
can be used, together with core.lambda.flip()
in higher order
functions when mapping over a collection, if you want to apply them to some
constant argument:
1 2 3 4 5 6 7 8 | var fns = [
function(a){ return a + 2 },
function(a){ return a - 2 },
function(a){ return a * 2 },
function(a){ return a / 2 }
]
fns.map(flip(apply)(3)) => [5, 1, 6, 1.5]
|
flip
¶core.lambda.
flip
(f)¶Returns: | The function f with parameters inverted. |
---|
(α → β → γ) → (β → α → γ)
Inverts the order of the parameters of a binary function.
1 2 3 4 | var subtract = function(a){ return function(b){ return a - b }}
subtract(3)(2) // => 1
flip(subtract)(3)(2) // => -1
|
Flip can be used to partially apply the second argument in a binary curried function. It makes it much easier to create new functionality, by just applying functions, rather than explicitly creating new ones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var divide = curry(2, function(a, b) {
return a / b
})
var dividedBy = curry(2, function(a, b) {
return b / a
})
var dividedBy5 = function(a) {
return divide(a, 5)
}
// Instead you could write:
var dividedBy = flip(divide)
var dividedBy5 = dividedBy(5)
|
compose
¶core.lambda.
compose
(f, g)¶Returns: | A composition of f and g . |
---|
(β → γ) → (α → β) → (α → γ)
Composes two functions together.
1 2 3 4 | function inc(a){ return a + 1 }
function square(a){ return a * a }
compose(inc)(square)(2) // => inc(square(2)) => 5
|
curry
¶core.lambda.
curry
(n, f)¶Returns: | A curried version of f , up to n arguments. |
---|
ₙ:Number → (α₁, α₂, ..., αₙ → β) → (α₁ → α₂ → ... → αₙ → β)
Transforms any function on tuples into a curried function.
1 2 3 4 5 6 7 | function sub3(a, b, c){ return a - b - c }
curry(3, sub3)(5)(2)(1) // => 2
curry(3, sub3)(5, 2)(1) // => 2
curry(3, sub3)(5)(2, 1) // => 2
curry(3, sub3)(5, 2, 1) // => 2
curry(3, sub3)(5, 2, 1, 0) // => TypeError: 2 is not a function
|
spread
¶core.lambda.
spread
(f, xs)¶Returns: | The result of applying the function f to arguments xs . |
---|
(α₁ → α₂ → ... → αₙ → β) → (#[α₁, α₂, ..., αₙ] → β)
Applies a list of arguments to a curried function.
var add = curry(2, function(a, b){ return a + b })
spread(add)([3, 2]) // => add(3)(2) => 5
uncurry
¶core.lambda.
uncurry
(f)¶Returns: | A function on tuples. |
---|
(α₁ → α₂ → ... → αₙ → β) → (α₁, α₂, ..., αₙ → β)
Transforms a curried function into a function on tuples.
var add = function(a){ return function(b){ return a + b }}
uncurry(add)(3, 2) // => add(3)(2) => 5
upon
¶core.lambda.
upon
(f, g)¶Returns: | A binary function f with arguments transformed by g . |
---|
(β → β → γ) → (α → β) → (α → α → γ)
Applies an unary function to both arguments of a binary function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Sorting an array of pairs by the first component
var curry = require('core.lambda').curry
var xss = [[1, 2], [3, 1], [-2, 4]]
function compare(a, b) {
return a < b? -1
: a === b? 0
: /* a> b */ 1
}
function first(xs) {
return xs[0]
}
function sortBy(f, xs) {
return xs.slice().sort(f)
}
var compareC = curry(2, compare)
sortBy(upon(compareC, first), xss) // => [[-2, 4], [1, 2], [3, 1]]
|
core.operators
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/core.operators/issues |
Version: | 1.0.0 |
Repository: | https://github.com/folktale/core.operators |
Portability: | Portable |
npm package: | core.operators |
Provides JS operators as curried functions.
Require the core.operators
package, after installing it:
var operators = require('core.operators')
JavaScript’s operators are not first-class functions, so using them in a higher-order function requires one to wrap the call at the call-site:
1 2 3 4 5 6 7 | var people = [
{ name: 'Bob', age: 14 },
{ name: 'Alice', age: 12 }
]
people.map(function(person){ return person.name })
// => ['Bob', 'Alice']
|
This defeats some of the compositional nature of functional programming. This module provides first-class, curried versions of these special operators that you can combine with the usual function composition operations:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var op = require('core.operators')
people.map(op.get('name'))
// => ['Bob', 'Alice']
function compare(a, b) {
return a > b? 1
: a === b? 0
: /* a < b */ -1
}
var lambda = require('core.lambda')
people.sort(lambda.upon(compare, op.get('age'))).map(op.get('name'))
// => ['Alice', 'Bob']
|
core.operators.
subtract
(a, b)¶Number → Number → Number
JavaScript’s subtraction (a - b
) operator.
core.operators.
divide
(a, b)¶Number → Number → Number
JavaScript’s division (a / b
) operator.
core.operators.
multiply
(a, b)¶Number → Number → Number
JavaScript’s multiplication (a * b
) operator.
core.operators.
get
(key, object)¶String → Object → α | Undefined
Property accessor (object[key]
).
core.operators.
has
(key, object)¶String → Object → Boolean
Tests the existence of a property in an object (key in object
).
core.operators.
isInstance
(constructor, a)¶Function → Object → Boolean
Instance check (a instanceof constructor
).
Provides operations for control-flow.
control.monads
¶Stability: | 1 - Experimental |
---|---|
Bug Tracker: | https://github.com/folktale/control.monads/issues |
Version: | 0.6.0 |
Repository: | https://github.com/folktale/control.monads |
Portability: | Portable |
npm package: | control.monads |
Common monadic combinators and sequencing operations.
Require the control.monads
package, after installing it:
var monads = require('control.monads')
control.monads.
sequence
(type, monads)¶Returns: | A monad containing an array of the values. |
---|
m:Monad(_) => m → Array(m(α)) → m(Array(α))
control.monads.
mapM
(type, transformation, values)¶Returns: | A monad containing an array of the values. |
---|
m:Monad(_) => m → (α → m(β)) → Array(α) → m(Array(β))
Converts each value into a monadic action, then evaluates such actions, left to right, and collects their results.
control.monads.
compose
(f, g, value)¶Returns: | A composition of the given functions on monads. |
---|
m:Monad(_) => (α → m(β)) → (β → m(γ)) → α → m(γ)
Left-to-right Kleisi composition of monads.
control.monads.
rightCompose
(f, g, value)¶Returns: | A composition of the given functions on monads. |
---|
m:Monad(_) => (β → m(γ)) → (α → m(β)) → α → m(γ)
Right-to-left Kleisi composition of monads.
control.monads.
join
(monad)¶Returns: | The nested monad. |
---|
m:Monad(_) => m(m(α)) → m(α)
Removes one level of nesting for a nested monad.
control.monads.
filterM
(type, predicate, values)¶Returns: | An array with values that pass the predicate, inside a monad. |
---|
m:Monad(_) => m → (α → m(Boolean)) → Array(α) → m(Array(α))
Filters the contents of an array with a predicate returning a monad.
control.monads.
concat
(left, right)¶Returns: | A new semigroup with the values combined. |
---|
s:Semigroup(_) => s(α) → s(α) → s(α)
Concatenates two semigroups.
control.monads.
map
(transformation, functor)¶Returns: | A functor with its contents transformed by f . |
---|
f:Functor(_) => (α → β) → f(α) → f(β)
Maps over a functor instance.
control.monads.
of
(value, type)¶Returns: | A new applicative instance containing the given value. |
---|
f:Applicative(_) => α → f → f(α)
Constructs a new applicative intance.
control.async
¶Stability: | 1 - Experimental |
---|---|
Bug Tracker: | https://github.com/folktale/control.async |
Version: | 0.5.1 |
Repository: | https://github.com/folktale/control.async |
Portability: | Portable |
npm package: | control.async |
Task(_, _) → AsyncModule
Operations for asynchronous control flow.
Require the control.async
package, after installing it, and give it a
valid data.task.Task
object to instantiate it:
1 2 | var Task = require('data.task')
var Async = require('control.async')(Task)
|
control.async.
parallel
(tasks)¶Returns: | A task that runs the given ones in parallel. |
---|
Array(Task(α, β)) → Task(α, Array(β))
Resolves all tasks in parallel, and collects their results.
control.async.
nondeterministicChoice
(tasks)¶Returns: | A task that selects the first task to resolve. |
---|
Array(Task(α, β)) → Task(α, Maybe(β))
Runs all tasks in parallel, selects the first one to either succeed or fail.
control.async.
choice
(tasks)¶Array(Task(α, β)) → Task(α, Maybe(β))
Alias for nondeterministicChoice()
control.async.
lift
(function)¶(α₁, α₂, ..., αₙ, (β → Unit)) → (α₁, α₂, ..., αₙ → Task(Unit, β))
Converts a function that takes a simple continuation to a Task.
control.async.
liftNode
(function)¶(α₁, α₂, ..., αₙ, (β, γ → Unit)) → (α₁, α₂, ..., αₙ → Task(β, γ))
Converts a function that takes a Node-style continuation to a Task.
control.async.
toNode
(task)¶Task(α, β) → (α | null, β | null → Unit)
Converts a Task to a Node-style function.
control.async.
fromPromise
(promise)¶Promise(α, β) → Task(α, β)
Converts a Promises/A+ to a Task.
control.async.
toPromise
(constructor, task)¶PromiseConstructor → Task(α, β) → Promise(α, β)
type PromiseConstructor = new((α → Unit), (β → Unit) → Unit)
→ Promise(α, β)
Converts from Task to Promises/A+.
Note
Do note that nested Tasks, unlike Promises/A+, are NOT
flattened. You need to manually call control.monads.join()
until you get to the value itself, if you care about passing
just the value.
control.async.
delay
(milliseconds)¶Returns: | A Task that succeeds after N milliseconds. |
---|
Number → Task(Unit, Number)
Constructs a Task that always succeeds after at least N milliseconds. The value of the Task will be the delta from the time of its initial execution to the time it gets resolved.
catchOnly
¶control.async.
catchOnly
(filter, task)¶(γ → Boolean) → Task(α, β) :: throws(γ) → Task(α | γ, β)
Reifies some errors thrown by the computation to a rejected task.
Ideally you wouldn’t care about reifying errors thrown by synchronous computations, but this might come in handy for some lifted computations.
catchAllPossibleErrors
¶control.async.
catchAllPossibleErrors
(task)¶Task(α, β) :: throws(Any) → Task(Any, β)
Reifies all errors thrown by the computation to a rejected task.
Ideally you wouldn’t care about reifying errors thrown by synchronous computations, but this might come in handy for some lifted computations.
Warning
Special care should be taken when using this method, since it’ll reify
*ALL* errors (for example, OutOfMemory errors, StackOverflow errors,
...), and it can potentially lead the whole system to an unstable
state. The catchOnly()
function is favoured over this one, since
you can decide which errors should be caught and reified in the task,
and have all the others crash the process as expected.
Provides functional (persistent and immutable) data structures for representing program data.
data.either
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/data.either/issues |
Version: | 1.2.0 |
Repository: | https://github.com/folktale/data.either |
Portability: | Portable |
npm package: | data.either |
A structure for disjunctions (e.g.: computations that may fail).
The Either(α, β)
structure represents the logical disjunction between
α
and β
. In other words, Either
may contain either a value of
type α
, or a value of type β
, at any given time, and it’s possible
to know which kind of value is contained in it.
This particular implementation is biased towards right values (β
), thus
common projections (e.g.: for the monadic instance) will take the right
value over the left one.
Require the data.either
package, after installing it:
var Either = require('data.either')
This gives you back an data.either.Either
object.
A common use of this structure is to represent computations that may fail when you want to provide additional information on the failure. This can force failures and their handling to be explicit, avoiding the problems associated with throwing exceptions: non locality, abnormal exits, unintended stack unwinding, etc.
Either
¶data.either.
Either
¶type Either(α, β) = Left(α) | Right(β)
implements
Applicative(β), Functor(β), Chain(β), Monad(β), ToString
Represents the logical disjunction between α
and β
.
Either.
Left
(value)¶α → Either(α, β)
Constructs a new Either(α, β)
structure holding a
Left
value. This usually represents a failure, due
to the right-bias of this structure.
Either.
Right
(value)¶β → Either(α, β)
Constructs a new Either(α, β)
structure holding a
Right
value. This usually represents a successful
value due to the right bias of this structure.
Either.
of
(value)¶β → Either(α, β)
Creates a new Either(α, β)
instance holding the
Right
value β
.
Either.prototype.
get
()¶Raises: |
|
---|
@Either(α, β) => Unit → β :: throws
Extracts the Right
value out of the Either(α, β)
structure, if it exists.
Either.prototype.
ap
(anApplicative)¶@Either(α, β → γ), f:Applicative(_) => f(β) → f(γ)
Applies the function inside the Either(α, β)
structure
to another Applicative type.
Either.prototype.
map
(transformation)¶@Either(α, β) => (β → γ) → Either(α, γ)
Transforms the Right
value of the Either(α, β)
structure
using a regular unary function.
Either.prototype.
chain
(transformation)¶@Either(α, β), m:Monad(_) => (β → m(γ)) → m(γ)
Transforms the Right
value of the Either(α, β)
structure
using an unary function over monads.
Either.prototype.
fold
(leftTransformation, rightTransformation)¶@Either(α, β) => (α → γ), (β → γ) → γ
Applies a function to each case in the data structure.
Either.prototype.
cata
(pattern)¶@Either(α, β) => { r | Pattern } → γ
type Pattern {
Left: α → γ,
Right: β → γ
}
Applies a function to each case in the data structure.
Either.prototype.
swap
()¶@Either(α, β) => Unit → Either(β, α)
Swaps the disjunction values.
Either.prototype.
bimap
(leftTransformation, rightTransformation)¶@Either(α, β) => (α → γ), (β → δ) → Either(γ, δ)
Maps both sides of the disjunction.
data.maybe
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/data.maybe/issues |
Version: | 1.2.0 |
Repository: | https://github.com/folktale/data.maybe |
Portability: | Portable |
npm package: | data.maybe |
A structure for values that may not be present, or computations that may fail.
The class models two different cases:
Just α
— represents aMaybe(α)
that contains a value.α
may be any value, includingnull
andundefined
.Nothing
— represents aMaybe(α)
that has no values. Or a failure that needs no additional information.
Require the data.maybe
package, after installing it:
var Maybe = require('data.maybe')
This gives you back a data.maybe.Maybe
object.
The Maybe(α)
structure explicitly models the effects that are implicit
in Nullable
types, thus has none of the problems associated with using
null
or undefined
, such as NullPointerException
or TypeError:
undefined is not a function
.
Common uses of this structure includes modelling values that may or may not
be present. For example, instead of having both a collection.has(a)
and
collection.get(a)
operation, one may have the collection.get(a)
operation return a Maybe(α)
value. This avoids a problem of data
incoherence (specially in asynchronous collections, where a value may be
added between a call to .has()
and .get()
!).
Another common usage is for modelling functions that might fail to provide a
value. E.g.: collection.find(predicate)
can safely return a Maybe(α)
instance, even if the collection allows nullable values.
Maybe
¶data.maybe.
Maybe
¶type Maybe(α) = Nothing | Just(α)
implements
Applicative(α), Functor(α), Chain(α), Monad(α), ToString
A structure for values that may not be present, or computations that may fail.
Maybe.
Nothing
()¶Unit → Maybe(α)
Constructs a new Maybe(α)
structure with an absent value.
Commonly used to represent a failure.
Maybe.
Just
(value)¶α → Maybe(α)
Constructs a new Maybe(α)
structure that holds the single
value α
. Commonly used to represent a success.
Maybe.
of
(value)¶α → Maybe(α)
Constructs a new Maybe(α)
structure that holds the single
value α
.
Maybe.
fromNullable
(value)¶α | null | undefined → Maybe(α)
Constructs a new Maybe(α)
value from a nullable type.
If the value is null
or undefined
, returns a Nothing
,
otherwise returns the value wrapped in a Just
.
Maybe.prototype.
ap
(anApplicative)¶@Maybe(α → β), f:Applicative(_) => f(α) → f(β)
Applies the function inside the structure to another Applicative type.
Maybe.prototype.
map
(transformation)¶@Maybe(α) => (α → β) → Maybe(β)
Transforms the value of this structure using a regular unary function.
Maybe.prototype.
chain
(transformation)¶@Maybe(α), m:Monad(_) => (α → m(β)) → m(β)
Transforms the value of this structure using an unary function over monads.
data.task
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/data.task/issues |
Version: | 3.0.0 |
Repository: | https://github.com/folktale/data.task |
Portability: | Portable |
npm package: | data.task |
A structure for time-dependent values, providing explicit effects for delayed computations, latency, etc.
Require the data.task
package, after installing it:
var Task = require('data.task')
This gives you back a data.task.Task
object.
This structure allows one to model side-effects (specially time-based ones) explicitly, such that one can have full knowledge of when they’re dealing with delayed computations, latency, or anything that isn’t pure or can be computed immediately.
A common use of this structure is to replace the usual Continuation-Passing Style form of programming in order to be able to compose and sequence time-dependent effects using the generic and powerful monadic operations.
Data.Task
can be used to model time-dependent
values.Task
¶data.task.
Task
¶type Task(α, β)
new ((α → Unit), (β → Unit) → γ), (γ → Unit)
implements
Chain(β), Monad(β), Functor(β), Applicative(β),
Semigroup(β), Monoid(β), ToString
A structure for time-dependent values.
Task.
of
(value)¶β → Task(α, β)
Constructs a new Task containing the given successful value.
Task.prototype.
map
(transformation)¶@Task(α, β) => (β → γ) → Task(α, γ)
Transforms the successful value of the Task using a regular unary function.
Task.prototype.
chain
(transformation)¶@Task(α, β) => (β → Task(α, γ)) → Task(α, γ)
Transforms the succesful value of the Task using a function over monads.
Task.prototype.
ap
(task)¶@Task(α, β → γ) => Task(α, β) → Task(α, γ)
Transforms a Task by applying the function inside this receiver.
Task.prototype.
orElse
(transformation)¶@Task(α, β) => (α → Task(γ, β)) → Task(γ, β)
Transforms the failure value of the Task into a new Task.
Task.prototype.
fold
(onRejection, onSucecss)¶@Task(α, β) => (α → γ), (β → γ) → Task(δ, γ)
Applies a function to each side of the task.
Task.prototype.
cata
(pattern)¶@Task(α, β) => { Rejected: α → γ, Resolved: β → γ } → Task(δ, γ)
Applies a function to each side of the task.
data.validation
¶Stability: | 3 - Stable |
---|---|
Bug Tracker: | https://github.com/folktale/data.validation/issues |
Version: | 1.3.0 |
Repository: | https://github.com/folktale/data.validation |
Portability: | Portable |
npm package: | data.validation |
A disjunction that is more appropriate for validating inputs and aggregating failures.
Require the data.validation
package, after installing it:
var Validation = require('data.validation')
This gives you back a data.validation.Validation
object.
The Validation(α, β)
is a disjunction that’s more appropriate for
validating inputs, and aggregating failures. It’s isomorphic to
data.either
, but provides better terminology for these use cases
(Failure
and Success
, versus Left
and Right
), and allows one
to aggregate failures and successes as an Applicative Functor.
Validation
¶data.validation.
Validation
¶type Validation(α, β) = Failure(α) | Success(β)
implements
Applicative(β), Functor(β), ToString
Represents the logical disjunction between α
and β
.
Validation.prototype.
isFailure
¶Boolean
True if the Validation(α, β)
contains a Failure
value.
Validation.
Failure
(value)¶α → Validation(α, β)
Constructs a new Validation structure holding a
Failure
value.
Validation.
Success
(value)¶β → Validation(α, β)
Constructs a new Validation structure holding a
Success
value.
Validation.
of
(value)¶β → Validation(α, β)
Creates a new Validation instance holding the
Success
value β
.
Validation.prototype.
get
()¶Raises: |
|
---|
@Validation(α, β) => Unit → β :: throws
Extracts the Success
value out of the Validation(α, β)
structure, if it exists.
Validation.prototype.
ap
(anApplicative)¶@Validation(α, β → γ), f:Applicative(_) => f(β) → f(γ)
Applies the function inside the Validation(α, β)
structure
to another Applicative type, and combines failures
with a semigroup.
Validation.prototype.
map
(transformation)¶@Validation(α, β) => (β → γ) → Validation(α, γ)
Transforms the Success
value of the Validation(α, β)
structure using a regular unary function.
Validation.prototype.
fold
(leftTransformation, rightTransformation)¶@Validation(α, β) => (α → γ), (β → γ) → γ
Applies a function to each case in the data structure.
Validation.prototype.
cata
(pattern)¶@Validation(α, β) => { r | Pattern } → γ
type Pattern {
Failure: α → γ,
Success: β → γ
}
Applies a function to each case in the data structure.
Validation.prototype.
swap
()¶@Validation(α, β) => Unit → Validation(β, α)
Swaps the disjunction values.
Validation.prototype.
bimap
(leftTransformation, rightTransformation)¶@Validation(α, β) => (α → γ), (β → δ) → Validation(γ, δ)
Maps both sides of the disjunction.