Showing preview only (549K chars total). Download the full file or copy to clipboard to get everything.
Repository: emmanueltouzery/prelude.ts
Branch: master
Commit: dbf2c898fbcc
Files: 64
Total size: 526.1 KB
Directory structure:
gitextract_tam3khkl/
├── .circleci/
│ └── config.yml
├── .gitignore
├── .npmignore
├── LICENSE.TXT
├── README.md
├── benchmarks/
│ └── bench.ts
├── package.json
├── package.json.txt
├── scripts/
│ ├── make_doc.sh
│ ├── make_doc_extra/
│ │ ├── classes.ts
│ │ ├── globals.ts
│ │ ├── helpers.ts
│ │ ├── make_doc_extra.ts
│ │ └── make_doc_preprocess.ts
│ ├── prepublish.sh
│ └── with_header.sh
├── src/
│ ├── ChromeDevToolFormatters.ts
│ ├── Collection.ts
│ ├── Comparison.ts
│ ├── Contract.ts
│ ├── Either.ts
│ ├── Foldable.ts
│ ├── Function.ts
│ ├── Future.ts
│ ├── HashMap.ts
│ ├── HashSet.ts
│ ├── IMap.ts
│ ├── ISet.ts
│ ├── Lazy.ts
│ ├── LinkedList.ts
│ ├── Option.ts
│ ├── Predicate.ts
│ ├── Seq.ts
│ ├── SeqHelpers.ts
│ ├── Stream.ts
│ ├── Tuple2.ts
│ ├── Value.ts
│ ├── Vector.ts
│ └── index.ts
├── tests/
│ ├── Collection.ts
│ ├── Comments.ts
│ ├── Comparison.ts
│ ├── DocLinks.ts
│ ├── Either.ts
│ ├── Function.ts
│ ├── Future.ts
│ ├── HashMap.ts
│ ├── HashSet.ts
│ ├── Lazy.ts
│ ├── LinkedList.ts
│ ├── Option.ts
│ ├── Predicate.ts
│ ├── SampleData.ts
│ ├── Seq.ts
│ ├── Stream.ts
│ ├── TestHelpers.ts
│ ├── Tuple2.ts
│ └── Vector.ts
├── tsconfig.benchmarks.json
├── tsconfig.docgen.json
├── tsconfig.json
├── tsconfig.prepublish.json
├── tsconfig.test.json
└── www_demo/
└── index.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: cimg/node:12.22
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package-lock.json" }}
# run tests!
- run: npm test
# apidoc generation can easily fail -- check it.
- run: npm run docgen
# check for bad links in the apidocs
- run: sh -c "! grep -r \"\[\[\w\" apidoc/"
================================================
FILE: .gitignore
================================================
dist/
node_modules/
apidoc/
npm-debug.log
tests/apidoc-*
yarn.lock
================================================
FILE: .npmignore
================================================
# Required so that built files are included when using this github repo directly as an npm dependency
#
# See https://stackoverflow.com/questions/40528053/npm-install-and-build-of-forked-github-repo/57829251#57829251
# for more details.
================================================
FILE: LICENSE.TXT
================================================
Copyright 2017 Emmanuel Touzery
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
================================================
FILE: README.md
================================================
# prelude-ts
[![NPM version][npm-image]][npm-url]
[![Tests][circleci-image]][circleci-url]
[![apidoc][apidoc-image]][apidoc-url]
## Intro
prelude-ts (previously prelude.ts) is a TypeScript library which aims to make functional programming
concepts accessible and productive in TypeScript. Note that even though it's
written in TypeScript, it's perfectly usable from JavaScript (including ES5)!
It provides [persistent](https://en.wikipedia.org/wiki/Persistent_data_structure)
immutable collections (Vector, Set, Map, Stream), and constructs such as Option,
Either, Predicate and Future.
```typescript
Vector.of(1,2,3)
.map(x => x*2)
.head()
// => Option.of(2)
Option.sequence(
Vector.of(Option.of(1), Option.of(2)))
// => Option.of(Vector.of(1,2))
Vector.of(1,2,3,4).groupBy(x => x%2)
// => HashMap.of([0, Vector.of(2,4)],[1, Vector.of(1,3)])
Vector.of(1,2,3).zip("a", "b", "c").takeWhile(([k,v]) => k<3)
// Vector.of([1,"a"],[2,"b"])
HashMap.of(["a",1],["b",2]).get("a")
// Option.of(1)
```
The collections are also JavaScript iterables, so if you have an ES6 runtime,
you can use the `for .. of` construct on them. If you're not familiar with
immutable collections, `list.append(newItem)` keeps `list` unchanged; `append()`
returns a new list. Immutability helps reasoning about code.
You can check the **[User Guide](https://github.com/emmanueltouzery/prelude-ts/wiki/Prelude%E2%88%92ts-user-guide)**,
and browse the **[API documentation](http://emmanueltouzery.github.io/prelude.ts/latest/apidoc/globals.html)**,
or our **[blog](http://emmanueltouzery.github.io/blog/tags/prelude.ts.html)**.
Note that the constructors are private, and you should use static methods to build
items — for instance: `Option.of`, `Vector.of`, `Vector.ofIterable`, and so on.
`HashSet` and `HashMap` are implemented using the
[HAMT algorithm](http://en.wikipedia.org/wiki/Hash_array_mapped_trie),
and concretely the [hamt_plus library](https://www.npmjs.com/package/hamt_plus).
`Vector` is implemented through a
[bit-mapped vector trie](http://hypirion.com/musings/understanding-persistent-vector-pt-1)
and concretely the [list library](https://github.com/funkia/list/), as of 0.7.7.
In addition, the library is written in idiomatic JavaScript style, with loops
instead of recursion, so the performance should be good
([see benchmarks here comparing to immutable.js and more](https://github.com/emmanueltouzery/prelude-ts/wiki/Benchmarks)).
`list` and `hamt_plus` are the two only dependencies of prelude-ts.
## Set, Map and equality
JavaScript doesn't have structural equality, except for primitive types.
So `1 === 1` is true, but `[1] === [1]` is not, and neither is `{a:1} === {a:1}`.
This poses problems for collections, because if you have a `Set`, you don't
want duplicate elements because of this limited definition of equality.
For that reason, prelude-ts encourages you to define for your non-primitive types
methods `equals(other: any): boolean` and `hashCode(): number` (the same
methods that [immutable.js uses](https://immutable-js.github.io/immutable-js/docs/#/ValueObject)).
With these methods, structural equality is achievable, and indeed
`Vector.of(1,2,3).equals(Vector.of(1,2,3))` is `true`. However this can only
work if the values you put in collections have themselves properly defined equality
([see how prelude-ts can help](https://github.com/emmanueltouzery/prelude-ts/wiki/Equality)).
If these values don't have structural equality, then we can get no better than
`===` behavior.
prelude-ts attempts to assist the programmer with this; it tries to encourage
the developer to do the right thing. It'll refuse types without obviously properly
defined equality in Sets and in Maps keys, so `HashSet.of([1])`,
or `Vector.of([1]).equals(Vector.of([2]))` will not compile.
For both of these, you get (a longer version of) this message:
Type 'number[]' is not assignable to type 'HasEquals'.
Property 'equals' is missing in type 'number[]'.
See the [User Guide](https://github.com/emmanueltouzery/prelude-ts/wiki/Prelude%E2%88%92ts-user-guide#equality)
for more details.
## Installation
TypeScript must know about `Iterable`, an ES6 feature (but present in most browsers)
to compile prelude-ts. If you use TypeScript and target ES5, a minimum change to your tsconfig.json
could be to add:
```json
"lib": ["DOM", "ES5", "ScriptHost", "es2015.iterable"]
```
(compared to the default ES5 settings it only adds 'es2015.iterable')
### Using in Node.js
Just add the dependency in your `package.json` and start using it (like
`import { Vector } from "prelude-ts";`, or `const { Vector } = require("prelude-ts");`
if you use commonjs).
Everything should work, including type-checking if you use TypeScript. prelude-ts also provides
pretty-printing in the node REPL.
### Using in the browser
Add the dependency in your `package.json`; TypeScript should automatically
register the type definitions.
The npm package contains the files `dist/src/prelude_ts.js` and `dist/src/prelude_ts.min.js`,
which are UMD bundles; they work with other module systems and set `prelude_ts`
as a window global if no module system is found. Include the relevant one in your
index.html in script tags:
```html
<script src="node_modules/prelude-ts/dist/src/prelude_ts.min.js"></script>
```
You shouldn't have an issue to import prelude-ts in your application, but if you use
modules it gets a little more complicated. One solution if you use them is to create
an `imports.d.ts` file with the following contents:
```typescript
// https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-283007750
import * as _P from 'prelude-ts';
export as namespace prelude_ts;
export = _P;
```
Then in a `.ts` file of your application, outside of a module, you can do:
```typescript
import Vector = prelude_ts.Vector;
```
\- to get values without the namespace.
Finally, if you also include `dist/src/chrome_dev_tools_formatters.js` through
a `script` tag, and [enable Chrome custom formatters](http://bit.ly/object-formatters),
then you can get
[a nice display of prelude-ts values in the Chrome debugger](https://raw.githubusercontent.com/wiki/emmanueltouzery/prelude-ts/chrome_formatters.png).
## Wishlist/upcoming features
* CharSeq, a string wrapper?
* Non-empty vector? (already have [non-empty linkedlist](http://emmanueltouzery.github.io/prelude.ts/latest/apidoc/classes/linkedlist.conslinkedlist.html))
* Make use of trampolining or a similar technique in `Stream` to avoid stack overflow exceptions on very large streams
* More functions on existing classes
## Out of scope for prelude-ts
* Free monads
* Monad transformers
* Effect tracking
* Higher-kinded types simulation
I think these concepts are not expressible in a good enough manner in a language
such as TypeScript.
## Alternatives and Influences
* [monet.js](https://monet.github.io/monet.js/) -- only has the `List` and
`Option` collections, implemented in functional-style ES5. The implementation,
using recursion, means its list type is noticeably slower than prelude-ts's.
* [immutable.js](https://immutable-js.github.io/immutable-js/) -- doesn't have the
`Option` concept; the types can be clunky.
* [sanctuary](https://github.com/sanctuary-js/sanctuary)
offers global functions like `S.filter(S.where(...))` while prelude-ts prefers a
fluent-API style like `list.filter(..).sortBy(...)`. Also, sanctuary doesn't
offer sets and maps. On the other hand, sanctuary has some JS runtime type system
support, which prelude-ts doesn't have.
* [ramdajs](http://ramdajs.com/) offers global functions like
`R.filter(R.where(...))` while prelude-ts prefers a
fluent-API style like `list.filter(..).sortBy(...)`. Also, ramda doesn't offer
sets and maps. Ramda also uses currying a lot out of the box, which may not
be intuitive to a number of developers. In prelude,
[currying](http://emmanueltouzery.github.io/prelude.ts/latest/apidoc/interfaces/function.function2.html#curried)
& [partial application](http://emmanueltouzery.github.io/prelude.ts/latest/apidoc/interfaces/function.function2.html#apply1)
are opt-in.
* [lodash](https://lodash.com) also has global functions, and many functions
mutate the collections.
* [vavr](http://www.vavr.io/) -- it's a Java library, but it's the main inspiration for prelude-ts.
Note that vavr is inspired by the Scala library, so prelude-ts also is,
transitively.
## TypeScript version
As of 0.8.2, prelude requires TypeScript 3.1 or newer.
## Commands
npm install
npm test
npm run-script docgen
npm run benchmarks
[npm-image]: https://img.shields.io/npm/v/prelude-ts.svg?style=flat-square
[npm-url]: https://www.npmjs.com/package/prelude-ts
[circleci-image]: https://circleci.com/gh/emmanueltouzery/prelude-ts.svg?style=shield
[circleci-url]: https://circleci.com/gh/emmanueltouzery/prelude-ts
[apidoc-image]: http://emmanueltouzery.github.io/prelude.ts/apidoc.svg
[apidoc-url]: http://emmanueltouzery.github.io/prelude.ts/latest/apidoc/globals.html
## Related projects
* [Prelude-IO](https://github.com/Annoiiyed/Prelude-IO), a library offering IO features -including deserializing and validation- on top of prelude-ts, emphasizing type-safety and immutability
================================================
FILE: benchmarks/bench.ts
================================================
const Benchmark: any = require('benchmark');
import { execSync } from "child_process";
import { Vector } from "../src/Vector";
import { HashSet } from "../src/HashSet";
import { HashMap } from "../src/HashMap";
import { LinkedList } from "../src/LinkedList";
// import immutables through require for now so that
// the immutables typescript type definitions are not
// parsed: as of typescript 2.9rc and immutables 4.0.0rc9
// they don't compile.
const imm: any = require('immutable');
// import * as imm from 'immutable';
const hamt: any = require("hamt_plus");
const hamtBase: any = require("hamt");
import * as Funkia from "list";
const lengths = [200, 10000];
function getPrerequisites(length:number): Prerequisites {
// https://stackoverflow.com/a/43044960/516188
const getArray = (length:number) => Array.from({length}, () => Math.floor(Math.random() * length));
const array = getArray(length);
const vec = Vector.ofIterable(array);
const rawhamt = hamt.empty.mutate(
(h:any) => {
const iterator = array[Symbol.iterator]();
let curItem = iterator.next();
while (!curItem.done) {
h.set(h.size, curItem.value);
curItem = iterator.next();
}
});
let rawhamtBase = hamtBase.empty;
const iterator = array[Symbol.iterator]();
let curItem = iterator.next();
while (!curItem.done) {
rawhamtBase = rawhamtBase.set(rawhamtBase.size, curItem.value);
curItem = iterator.next();
}
const list = LinkedList.ofIterable(array);
const immList = imm.List(array);
const funkiaList = Funkia.from(array);
const idxThreeQuarters = array.length*3/4;
const atThreeQuarters = array[idxThreeQuarters];
const hashset = HashSet.ofIterable(array);
const immSet = imm.Set(array);
const hashmap = HashMap.ofIterable<string,number>(array.map<[string,number]>(x => [x+"",x]));
const immMap = imm.Map(array.map<[string,number]>(x => [x+"",x]));
return {vec,immList,array,list,idxThreeQuarters,
rawhamt,rawhamtBase,hashset,immSet,length,hashmap,immMap, funkiaList};
}
interface Prerequisites {
vec: Vector<number>;
// immList: imm.List<number>;
immList: any;
array: number[];
list:LinkedList<number>;
idxThreeQuarters: number;
rawhamt: any;
rawhamtBase: any;
hashset: HashSet<number>;
// immSet: imm.Set<number>;
immSet: any;
hashmap: HashMap<string,number>;
// immMap: imm.Map<string,number>;
immMap: any;
length:number;
funkiaList: Funkia.List<number>;
}
const preReqs = lengths.map(getPrerequisites);
function _compare(preReqs: Prerequisites, items: Array<[string, (x:Prerequisites)=>any]>) {
const benchSuite: any = new Benchmark.Suite;
for (const item of items) {
benchSuite.add(item[0], () => item[1](preReqs));
}
benchSuite.on('cycle', function(event:any) {
console.log(String(event.target));
}).on('complete', function(this:any) {
console.log('Fastest is ' + this.filter('fastest').map('name') + "\n");
}).run();
}
function compare(...items: Array<[string, (x:Prerequisites)=>any]>) {
for (let i=0;i<lengths.length;i++) {
let length = lengths[i];
console.log("n = " + length);
_compare(preReqs[i], items);
}
}
console.log("immList, immSet are the immutablejs list,set... https://facebook.github.io/immutable-js/");
console.log("funkiaList is the list from https://github.com/funkia/list");
process.stdout.write("node version: ");
execSync("node --version", {stdio:[0,1,2]});
console.log();
compare(['Vector.toArray', (p:Prerequisites) => p.vec.toArray()],
['LinkedList.toArray', (p:Prerequisites) => p.list.toArray()],
['immList.toArray', (p:Prerequisites) => p.immList.toArray()],
['funkiaList.toArray', (p:Prerequisites) => Funkia.toArray(p.funkiaList)]);
compare(['Vector.take', (p:Prerequisites) => p.vec.take(p.idxThreeQuarters)],
['Array.slice', (p:Prerequisites) => p.array.slice(0,p.idxThreeQuarters)],
['immList.take', (p:Prerequisites) => p.immList.take(p.idxThreeQuarters)],
['LinkedList.take', (p:Prerequisites) => p.list.take(p.idxThreeQuarters)],
['funkiaList.take', (p:Prerequisites) => Funkia.take(p.idxThreeQuarters, p.funkiaList)]);
compare(['Vector.drop', (p:Prerequisites) => p.vec.drop(p.idxThreeQuarters)],
['Array.slice', (p:Prerequisites) => p.array.slice(p.idxThreeQuarters)],
['immList.slice', (p:Prerequisites) => p.immList.slice(p.idxThreeQuarters)],
['LinkedList.drop', (p:Prerequisites) => p.list.drop(p.idxThreeQuarters)],
['funkiaList.drop', (p:Prerequisites) => Funkia.drop(p.idxThreeQuarters, p.funkiaList)]);
compare(['Vector.filter', (p:Prerequisites) => p.vec.filter(x => x%2===0)],
['Array.filter', (p:Prerequisites) => p.array.filter(x => x%2===0)],
['immList.filter', (p:Prerequisites) => p.immList.filter((x:number) => x%2===0)],
['LinkedList.filter', (p:Prerequisites) => p.list.filter(x => x%2===0)],
['funkiaList.filter', (p:Prerequisites) => Funkia.filter(x => x%2===0, p.funkiaList)]);
compare(['Vector.map', (p:Prerequisites) => p.vec.map(x => x*2)],
['Array.map', (p:Prerequisites) => p.array.map(x => x*2)],
['immList.map', (p:Prerequisites) => p.immList.map((x:number) => x*2)],
['LinkedList.map', (p:Prerequisites) => p.list.map(x => x*2)],
['funkiaList.map', (p:Prerequisites) => Funkia.map(x => x*2, p.funkiaList)]);
compare(['Vector.find', (p:Prerequisites) => p.vec.find(x => x===p.idxThreeQuarters)],
['Array.find', (p:Prerequisites) => p.array.find(x => x===p.idxThreeQuarters)],
['immList.find', (p:Prerequisites) => p.immList.find((x:number) => x===p.idxThreeQuarters)],
['LinkedList.find', (p:Prerequisites) => p.list.find(x => x===p.idxThreeQuarters)],
['funkiaList.find', (p:Prerequisites) => Funkia.find(x => x===p.idxThreeQuarters, p.funkiaList)]);
compare(['Vector.ofIterable', (p:Prerequisites) => Vector.ofIterable(p.array)],
['rawhamt.build from iterable', (p:Prerequisites) => {
hamt.empty.mutate(
(h:any) => {
const iterator = p.array[Symbol.iterator]();
let curItem = iterator.next();
while (!curItem.done) {
h.set(h.size, curItem.value);
curItem = iterator.next();
}
})
}],
['rawhamt.build from array', (p:Prerequisites) => {
hamt.empty.mutate(
(h:any) => {
for (let i=0;i<p.array.length;i++) {
h.set(i, p.array[i]);
}
})
}],
['rawhamtBase.build from iterable', (p:Prerequisites) => {
let rawhamtBase = hamtBase.empty;
const iterator = p.array[Symbol.iterator]();
let curItem = iterator.next();
while (!curItem.done) {
rawhamtBase = rawhamtBase.set(rawhamtBase.size, curItem.value);
curItem = iterator.next();
}
}],
['LinkedList.ofIterable', (p:Prerequisites) =>LinkedList.ofIterable(p.array)],
['immList.ofIterable', (p:Prerequisites) => imm.List(p.array)],
['funkiaList.ofArray', (p:Prerequisites) => Funkia.from(p.array)]);
compare(['Vector.get(i)', (p:Prerequisites) => p.vec.get(p.length/2)],
['rawhamt.get(i)', (p:Prerequisites) => p.rawhamt.get(p.length/2)],
['rawhamtBase.get(i)', (p:Prerequisites) => p.rawhamtBase.get(p.length/2)],
['LinkedList.get(i)', (p:Prerequisites) => p.list.get(p.length/2)],
['Array.get(i)', (p:Prerequisites) => p.array[p.length/2]],
['immList.get(i)', (p:Prerequisites) => p.immList.get(p.length/2)],
['funkiaList.get(i)', (p:Prerequisites) => Funkia.nth(p.length/2, p.funkiaList)]);
compare(['Vector.flatMap', (p:Prerequisites) => p.vec.flatMap(x => Vector.of(1,2))],
['LinkedList.flatMap', (p:Prerequisites) => p.list.flatMap(x =>LinkedList.of(1,2))],
['immList.flatMap', (p:Prerequisites) => p.immList.flatMap((x:number) => imm.List([1,2]))],
['funkiaList.chain', (p:Prerequisites) => Funkia.chain(x => Funkia.list(1,2), p.funkiaList)]);
compare(['Vector.reverse', (p:Prerequisites) => p.vec.reverse()],
['Array.reverse', (p:Prerequisites) => p.array.reverse()],
['immList.reverse', (p:Prerequisites) => p.immList.reverse()],
['LinkedList.reverse', (p:Prerequisites) => p.list.reverse()],
['funkiaList.reverse', (p:Prerequisites) => Funkia.reverse(p.funkiaList)]);
compare(['Vector.groupBy', (p:Prerequisites) => p.vec.groupBy(x => x%2)],
['LinkedList.groupBy', (p:Prerequisites) => p.list.groupBy(x => x%2)],
['immList.groupBy', (p:Prerequisites) => p.immList.groupBy((x:number) => x%2)]);
compare(
['Vector.append', (p:Prerequisites) => {
let v = Vector.empty<number>();
for (let item of p.array) {
v = v.append(item);
}
}],
['Array.push', (p:Prerequisites) => {
let v = [];
for (let item of p.array) {
v.push(item);
}
}],
['immList.push', (p:Prerequisites) => {
// let v = imm.List<number>();
let v = imm.List();
for (let item of p.array) {
v = v.push(item);
}
}],
['LinkedList.append', (p:Prerequisites) => {
let v = LinkedList.empty<number>();
for (let item of p.array) {
v = v.append(item);
}
}],
['Funkia.append', (p:Prerequisites) => {
let v = Funkia.empty();
for (let item of p.array) {
v = Funkia.append(item, v);
}
}]);
compare(
['Vector.prepend', (p:Prerequisites) => {
let v = Vector.empty<number>();
for (let item of p.array) {
v = v.prepend(item);
}
}],
['Array.unshift', (p:Prerequisites) => {
let v = [];
for (let item of p.array) {
v.unshift(item);
}
}],
['immList.unshift', (p:Prerequisites) => {
// let v = imm.List<number>();
let v = imm.List();
for (let item of p.array) {
v = v.unshift(item);
}
}],
['LinkedList.prepend', (p:Prerequisites) => {
let v = LinkedList.empty<number>();
for (let item of p.array) {
v = v.prepend(item);
}
}],
['Funkia.prepend', (p:Prerequisites) => {
let v = Funkia.empty();
for (let item of p.array) {
v = Funkia.prepend(item, v);
}
}]);
function iterateOn<T>(coll: Iterable<T>) {
const it = coll[Symbol.iterator]();
let item = it.next();
while (!item.done) {
item = it.next();
}
}
compare(['Vector.iterate', (p:Prerequisites) => iterateOn(p.vec)],
['Array.iterate', (p:Prerequisites) => iterateOn(p.array)],
['immList.iterate', (p:Prerequisites) => iterateOn(p.immList)],
['LinkedList.iterate', (p:Prerequisites) => iterateOn(p.list)],
['Funkia.iterate', (p:Prerequisites) => iterateOn(p.funkiaList)]);
compare(['Vector.appendAll', (p:Prerequisites) => p.vec.appendAll(p.vec)],
['Array.appendAll', (p:Prerequisites) => p.array.concat(p.array)],
['immList.appendAll', (p:Prerequisites) => p.immList.concat(p.immList)],
['LinkedList.appendAll', (p:Prerequisites) => p.list.appendAll(p.list)],
['Funkia.concat', (p:Prerequisites) => Funkia.concat(p.funkiaList, p.funkiaList)]);
compare(['Vector.prependAll', (p:Prerequisites) => p.vec.prependAll(p.vec)],
['Array.prependAll', (p:Prerequisites) => p.array.concat(p.array)],
['LinkedList.prependAll', (p:Prerequisites) => p.list.prependAll(p.list)]);
compare(['Vector.foldLeft', (p:Prerequisites) => p.vec.foldLeft(0, (acc,i)=>acc+i)],
['Array.foldLeft', (p:Prerequisites) => p.array.reduce((acc,i)=>acc+i)],
['immList.foldLeft', (p:Prerequisites) => p.immList.reduce((acc:number,i:number)=>acc+i,0)],
['LinkedList.foldLeft', (p:Prerequisites) => p.vec.foldLeft(0, (acc,i)=>acc+i)],
['Funkia.foldl', (p:Prerequisites) => Funkia.foldl((i,acc)=>acc+i, 0, p.funkiaList)]);
compare(['Vector.foldRight', (p:Prerequisites) => p.vec.foldRight(0, (i,acc)=>acc+i)],
['immList.foldRight', (p:Prerequisites) => p.immList.reduceRight((acc:number,i:number)=>acc+i,0)],
['LinkedList.foldRight', (p:Prerequisites) => p.vec.foldRight(0, (i,acc)=>acc+i)],
['Funkia.foldr', (p:Prerequisites) => Funkia.foldr((i,acc)=>acc+i, 0, p.funkiaList)]);
compare(['HashSet.ofIterable', (p:Prerequisites) => HashSet.ofIterable(p.array)],
['immSet', (p:Prerequisites) => imm.Set(p.array)]);
compare(['HashSet.ofIterable (from vector)', (p:Prerequisites) => HashSet.ofIterable(p.vec)],
['immSet (from vector)', (p:Prerequisites) => imm.Set(p.vec)]);
compare(['HashSet.contains', (p:Prerequisites) => p.hashset.contains(p.array[Math.floor(Math.random()*p.array.length)])],
['immSet.contains', (p:Prerequisites) => p.immSet.contains(p.array[Math.floor(Math.random()*p.array.length)])]);
compare(['HashSet.filter', (p:Prerequisites) => p.hashset.filter(x => x<p.length/2)],
['immSet.filter', (p:Prerequisites) => p.immSet.filter((x:number) => x<p.length/2)]);
compare(['HashMap.ofIterable', (p:Prerequisites) =>
HashMap.ofIterable<string,number>(p.array.map<[string,number]>(x => [x+"",x]))],
['immMap', (p:Prerequisites) => imm.Map(p.array.map<[string,number]>(x => [x+"",x]))]);
compare(['HashMap.ofIterable (from vector)', (p:Prerequisites) =>
HashMap.ofIterable(p.vec.map<[string,number]>(x => [x+"",x]))],
['immMap (from vector)', (p:Prerequisites) =>
imm.Map(p.vec.map<[string,number]>(x => [x+"",x]))]);
compare(['HashMap.get', (p:Prerequisites) => p.hashmap.get(p.array[Math.floor(Math.random()*p.array.length)]+"")],
['immMap.get', (p:Prerequisites) => p.immMap.get(p.array[Math.floor(Math.random()*p.array.length)]+"")]);
compare(['HashMap.filter', (p:Prerequisites) => p.hashmap.filter((k,v) => parseInt(k)<p.length/2)],
['immMap.filter', (p:Prerequisites) => p.immMap.filter((v:number,k:string) => parseInt(k)<p.length/2)]);
================================================
FILE: package.json
================================================
{
"name": "prelude-ts",
"version": "1.0.6",
"description": "A typescript functional programming library",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/emmanueltouzery/prelude.ts.git"
},
"dependencies": {
"hamt_plus": "1.0.2",
"list": "2.0.19"
},
"devDependencies": {
"@types/mocha": "5.2.7",
"mocha": "6.2.0",
"typedoc": "0.7.2",
"@types/node": "12.7.3",
"benchmark": "2.1.4",
"typescript": "3.1.6",
"browserify": "16.5.0",
"uglify-js": "3.6.0",
"immutable": "4.0.0-rc.12",
"hamt": "2.2.2",
"mocha-testcheck": "1.0.0-rc.0",
"request-promise-native": "1.0.7",
"@types/request-promise-native": "1.0.16",
"request": "2.88.0",
"handlebars": "4.5.3"
},
"keywords": [
"typescript",
"functional-programming",
"immutable",
"collections"
],
"author": "Emmanuel Touzery",
"license": "ISC",
"bugs": {
"url": "https://github.com/emmanueltouzery/prelude.ts/issues"
},
"homepage": "https://github.com/emmanueltouzery/prelude.ts#readme",
"scripts": {
"prepare": "npm run clean; scripts/prepublish.sh",
"test": "rm -f tests/apidoc-*; tsc -p tsconfig.test.json && node ./dist/tests/Comments.js && tsc -p tsconfig.test.json && ./node_modules/mocha/bin/mocha --throw-deprecation --timeout 90000 ./dist/tests/*.js",
"clean": "rm -f tests/apidoc-*; rm -Rf ./dist",
"docgen": "./scripts/make_doc.sh",
"benchmarks": "tsc -p tsconfig.benchmarks.json && node ./dist/benchmarks/bench.js"
},
"files": [
"dist",
"src",
"LICENSE.TXT",
"README.md",
"package.json"
]
}
================================================
FILE: package.json.txt
================================================
Comments for the package.json cause I can't put them in the file itself...
"@types/node": "9.6.7"
=> pin the types/node dependency for a more reliable build
example https://github.com/DefinitelyTyped/DefinitelyTyped/issues/25342
@types/node is a transitive dep from typedoc, typedoc doesn't lock it
the typescript dependency is at 3.1.x because that's the oldest TS version
that we support. You can also build with the TS version of your choice by
running 'tsc' manually.
we are modifying the generated typedoc HTML to improve it, hence did not
upgrade typedoc for a while.
Direct handlebars dependency:
https://github.com/TypeStrong/typedoc/issues/1159
Need to pin handlebars to unbreak typedoc
================================================
FILE: scripts/make_doc.sh
================================================
#!/usr/bin/env bash
set -e
npm install --no-package-lock
tsc -p tsconfig.docgen.json
# https://github.com/TypeStrong/typedoc/issues/564
# i would like typedoc to group functions in categories but it's not supported
# yet. So I hack it with their support for external modules...
# we'll modify the files and then revert our changes using
# git reset --HARD so make sure there are no local changes
if [[ $(git status --porcelain) ]]; then
echo "Can't generate the docs when the git checkout isn't clean"
exit 1
fi
# pre-process files
node dist/scripts/make_doc_extra/make_doc_preprocess.js
# trick for the 'Option' & 'Either' constants which typedoc skips as it clashes
# with the 'Option' & 'Either' type synomym
sed -i "s/const Option/const optionGlabiboulga/" src/Option.ts
sed -i "s/const Either/const eitherGlabiboulga/" src/Either.ts
sed -i "s/const LinkedList/const linkedListGlabiboulga/" src/LinkedList.ts
sed -i "s/const Stream/const streamGlabiboulga/" src/Stream.ts
sed -i "s/const Function0/const function0Glabiboulga/" src/Function.ts
sed -i "s/const Function1/const function1Glabiboulga/" src/Function.ts
sed -i "s/const Function2/const function2Glabiboulga/" src/Function.ts
sed -i "s/const Function3/const function3Glabiboulga/" src/Function.ts
sed -i "s/const Function4/const function4Glabiboulga/" src/Function.ts
sed -i "s/const Function5/const function5Glabiboulga/" src/Function.ts
sed -i "s/const Predicate/const predicateGlabiboulga/" src/Predicate.ts
# generate with typedoc
./node_modules/typedoc/bin/typedoc --exclude "**/make_doc_extra/*.ts" --mode file --out apidoc --excludePrivate --excludeExternals --excludeNotExported --ignoreCompilerErrors --tsconfig tsconfig.prepublish.json src/index.ts
# revert the 'Option' & 'Either' constant rename
find apidoc -name "*.html" -exec sed -i 's/optionglabiboulga/Option/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/option<wbr>Glabiboulga/Option/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/eitherglabiboulga/Either/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/either<wbr>Glabiboulga/Either/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/linkedlistglabiboulga/LinkedList/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/linked<wbr>List<wbr>Glabiboulga/LinkedList/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/streamglabiboulga/Stream/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/stream<wbr>Glabiboulga/Stream/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function0glabiboulga/Function0/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function0<wbr>Glabiboulga/Function0/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function1glabiboulga/Function1/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function1<wbr>Glabiboulga/Function1/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function2glabiboulga/Function2/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function2<wbr>Glabiboulga/Function2/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function3glabiboulga/Function3/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function3<wbr>Glabiboulga/Function3/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function4glabiboulga/Function4/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function4<wbr>Glabiboulga/Function4/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function5glabiboulga/Function5/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function5<wbr>Glabiboulga/Function5/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/predicateglabiboulga/Predicate/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/predicate<wbr>Glabiboulga/Predicate/g' \{\} \;
# modify the output to say 'File' instead of 'Module'
find apidoc -name "*.html" -exec sed -i 's/Module/File/g' \{\} \;
# modify the paths to say 'files' instead of 'modules'
mv apidoc/modules apidoc/files
find apidoc -name "*.html" -exec sed -i 's/modules/files/g' \{\} \;
node dist/scripts/make_doc_extra/make_doc_extra.js
# we're happy with the output now, revert the changes I made
# to the files to make typedoc think they're external modules
git reset --hard HEAD
================================================
FILE: scripts/make_doc_extra/classes.ts
================================================
import { readdirSync, writeFileSync } from "fs";
import { Vector } from "../../src/Vector";
import { Function4 } from "../../src/Function";
import { Predicate } from "../../src/Predicate";
import * as helpers from "./helpers";
const classesFolder = "apidoc/classes/";
/**
* we work on lines-level in the text output, not xml tags
* sometimes we have in one parentne "<parent>..</parent>"
* but we also want to support "<parent>\n...\n</parent>"
*/
function putStaticMethodsOnTop(parentTag: string, parentMarker: string, childTag: string,
linesByIndent: helpers.LinesByIndent): helpers.LinesByIndent
{
const [beforeIndexContent,indexContent,afterIndexContent] =
helpers.linesByIndentGetTagContents(linesByIndent, parentTag, l => l.indexOf(parentMarker) >= 0);
const openTag = Predicate.of<helpers.LineByIndent>(
l => l.contents.indexOf("<" + childTag) >= 0);
// the marker parent might be of the same tag, so keep it always.
const beforeFirstTag = indexContent.take(1)
.appendAll(indexContent.drop(1).takeWhile(openTag.negate()));
// the marker parent might be of the same tag, so keep it always.
const afterLastTag = indexContent.reverse().take(1)
.appendAll(indexContent
.reverse().drop(1)
.takeWhile(l => l.contents.indexOf("</" + childTag) < 0))
.reverse();
const staticOrNot = indexContent
.drop(beforeFirstTag.length())
.dropRight(afterLastTag.length())
// group the li tags and their contents together, even if they span
// across several lines like <parent>\n..\n</parent>
.foldLeft(Vector.empty<helpers.LinesByIndent>(), (sofar,cur) => openTag(cur) ?
// open li tag => start a new entry
sofar.append(Vector.of<helpers.LineByIndent>(cur)) :
// not open li tag => continue the previous entry
sofar.dropRight(1).append(sofar.last().getOrThrow().append(cur)))
.partition(l => l.head().getOrThrow().contents.indexOf("tsd-is-static") >= 0);
return beforeIndexContent
.appendAll(beforeFirstTag)
.appendAll(staticOrNot[0].flatMap(x=>x))
.appendAll(staticOrNot[1].flatMap(x=>x))
.appendAll(afterLastTag)
.appendAll(afterIndexContent);
}
function classPutStaticMethodsOnTop(classfilename: string): void {
const lines = helpers.fileGetLinesByIndent(classesFolder + classfilename);
const fnStatic = Function4.of(putStaticMethodsOnTop);
const transform = fnStatic.apply3("ul", "tsd-index-list", "li")
.andThen(fnStatic.apply3("li", "current tsd-kind-class", "li"))
.andThen(fnStatic.apply3("section", "tsd-panel-group tsd-member-group", "section"));
writeFileSync(classesFolder + classfilename, helpers.linesByIndentStr(transform(lines)));
}
export function putClassStaticMethodsOnTop(): void {
const classFiles = Vector.ofIterable(readdirSync(classesFolder));
classFiles.forEach(classPutStaticMethodsOnTop);
}
================================================
FILE: scripts/make_doc_extra/globals.ts
================================================
import { Vector } from "../../src/Vector";
import { HashMap } from "../../src/HashMap";
import { HashSet } from "../../src/HashSet";
import { writeFileSync } from "fs";
import * as helpers from "./helpers";
// group classes & interfaces by category.
const CATEGORIES = Vector.of<[string,Vector<string>]>(
["Control", Vector.of(
"Either", "Option", "Lazy",
"Function", "Predicate", "Future")],
["Collection", Vector.of(
"Collection", "Foldable",
"IMap","IterableArray","HashMap", "ISet", "HashSet",
"Seq", "LinkedList", "Stream", "Vector",
"Tuple2", "SortOnSpec", "SortBySpec")],
["Core", Vector.of(
"Comparison", "Value", "Contract")]);
function getSectionHeader(sectionName:string): string {
return `${helpers.indent(6)}<section class="tsd-index-section">` +
`${helpers.indent(7)}<h3>${sectionName}</h3>` +
`${helpers.indent(7)}<ul class="tsd-index-list">\n`;
}
function getSectionFooter(): string {
return `${helpers.indent(7)}</ul>` +
`${helpers.indent(6)}</section>`;
}
// improve the typedoc 'globals' screen by
// grouping the classes, interfaces by "categories"
// and not by type (class, interface,...) as typedoc
// does out of the box.
// i should rather try to improve typedoc but..
//
// I rather not using typescript external modules
// because there are several options and I think
// that may assume too much about how users of the
// library want to consume it.
// So everything is in the base namespace, but I can
// at least do some grouping in the apidocs.
export function groupGlobalsByCategory(): void {
// we'll modify the 'globals' typedoc file.
const lines = helpers.fileGetLinesByIndent("apidoc/globals.html");
// i'm interested in the part within 'tsd-index-content'
const [beforeIndexContent,indexContent,afterIndexContent] =
helpers.linesByIndentGetTagContents(lines, "div", l => l.indexOf("tsd-index-content") >= 0);
// right now i have the part of interest. I can regenerate the wrapping items
// the items which interest me are the leaves, which are <li> tags,
// and I'll group them by their name (class name, interface name...).
const liRows = indexContent
.map(l => l.contents)
.filter(t => t.indexOf("<li") >= 0)
.arrangeBy(row => helpers.requireNotNull(row.match(/>([\w<>]+)<\//))[1].replace(/<wbr>/g,""))
.getOrThrow("globals.arrangeBy failed!");
// start preparing the new contents for the indexContent
let newIndexContent = `${helpers.indent(5)}<div class="tsd-index-content">`;
const allKnownElements = CATEGORIES
.map(c=>c[1])
.flatMap(x=>x)
.transform(HashSet.ofIterable);
const missingElements = liRows.keySet().diff(allKnownElements);
if (!missingElements.isEmpty()) {
throw "Missing the following elements: " + missingElements;
}
CATEGORIES.forEach(([name,elements]) => {
newIndexContent += getSectionHeader(name);
const rows = elements.map(elt => liRows.get(elt).getOrThrow("can't find row for " + elt));
newIndexContent += rows.mkString("\n");
newIndexContent += getSectionFooter();
});
// conclude the new contents for the indexContent
newIndexContent += `${helpers.indent(5)}</div>\n`; // close tsd-index-content
// overwrite globals.html -- first the text before the indexContent,
// then the modified indexContent, then the rest.
writeFileSync(
'apidoc/globals.html',
helpers.linesByIndentStr(beforeIndexContent) +
newIndexContent +
helpers.linesByIndentStr(afterIndexContent));
}
================================================
FILE: scripts/make_doc_extra/helpers.ts
================================================
import { Stream } from "../../src/Stream";
import { Vector } from "../../src/Vector";
import { readFileSync } from "fs";
export function requireNotNull<T>(x:T|null): T {
if (x === null) {
throw "requireNotNull got null!";
}
return <T>x;
}
export function indent(count: number): string {
return "\n" + Stream.continually(()=>"\t")
.take(count).mkString("");
}
export type LineByIndent = {contents:string,indent:number};
export type LinesByIndent = Vector<LineByIndent>;
export function fileGetLinesByIndent(fname: string): LinesByIndent {
const contents = readFileSync(fname).toString();
return Vector.ofIterable(contents.split("\n"))
.map(l => ({indent: l.length-l.trim().length, contents: l}));
}
/**
* extract a tag from the linesbyindent. You tell me the tag name,
* how to match the start tag, I return to you the rows before, the rows within that
* tag (depth>=tagDepth) and the rows after.
* returns [before, atTag, after]
*/
export function linesByIndentGetTagContents(
lines: LinesByIndent,
tagName: string,
startPredicate: (line:string)=>boolean): [LinesByIndent, LinesByIndent, LinesByIndent] {
// split the parts before & after using string the predicate & indentation.
const [before, atTag] = lines.span(l => !startPredicate(l.contents));
const indentAtTag = atTag.head().getOrThrow().indent;
const [tagContents,after] =
atTag.span(l => (l.indent > indentAtTag) ||
(l.indent <= indentAtTag && l.contents.indexOf("</" + tagName) < 0));
if (!after.isEmpty()) {
// i want the closing tag also in 'atTag' but I rejected it in the span,
// so take it from after
return [before, tagContents.append(after.head().getOrThrow()), after.drop(1)];
}
return [before, tagContents, after];
}
export function linesByIndentStr(linesByIndent: LinesByIndent): string {
return linesByIndent.map(l => l.contents).mkString("\n");
}
================================================
FILE: scripts/make_doc_extra/make_doc_extra.ts
================================================
import { groupGlobalsByCategory } from "./globals";
import { putClassStaticMethodsOnTop } from "./classes";
console.log("groupGlobalsByCategory");
groupGlobalsByCategory();
console.log("putClassStaticMethodsOnTop");
putClassStaticMethodsOnTop();
console.log("make extra: done!");
================================================
FILE: scripts/make_doc_extra/make_doc_preprocess.ts
================================================
import { readFileSync, writeFileSync } from "fs";
declare global {
interface String {
startsWith(other:string): boolean
}
}
function makeModule(moduleName:string, filename:string): void {
const contents = readFileSync(filename).toString();
const moduleStart = "export module " + moduleName+ " { ";
const contentsWithModuleHeader = contents.trim().startsWith("/**") ?
// add the module header at the end of the apidoc comment
// so the comment apidoc covers the module
contents.replace(/\*\//, "*/ " + moduleStart) :
// add the module header straight at the top of the file
moduleStart + contents;
// in any case close at the end of the file
writeFileSync(filename, contentsWithModuleHeader + "\n}\n");
}
// list of files for which to trick typedoc
// to think they're external modules
makeModule("Comparison", "src/Comparison.ts");
makeModule("Contract", "src/Contract.ts");
makeModule("Option", "src/Option.ts");
makeModule("Either", "src/Either.ts");
makeModule("LinkedList", "src/LinkedList.ts");
makeModule("Stream", "src/Stream.ts");
makeModule("Function", "src/Function.ts");
makeModule("Predicate", "src/Predicate.ts");
================================================
FILE: scripts/prepublish.sh
================================================
#!/usr/bin/env bash
set -e
node_modules/typescript/bin/tsc -p tsconfig.prepublish.json
node ./node_modules/browserify/bin/cmd.js -s prelude_ts dist/src/index.js -o /tmp/prelude_ts_pre.js
scripts/with_header.sh /tmp/prelude_ts_pre.js > dist/src/prelude_ts.js
node ./node_modules/browserify/bin/cmd.js -s prelude_ts_object_formatters dist/src/ChromeDevToolFormatters.js -o dist/src/chrome_dev_tools_formatters.js
./node_modules/uglify-js/bin/uglifyjs --compress --mangle --output /tmp/prelude_ts_premin.js -- /tmp/prelude_ts_pre.js
scripts/with_header.sh /tmp/prelude_ts_premin.js > dist/src/prelude_ts.min.js
rm /tmp/prelude_ts_pre.js /tmp/prelude_ts_premin.js
================================================
FILE: scripts/with_header.sh
================================================
#!/usr/bin/env bash
set -e
cat <<END
/**
* prelude-ts v$(node -p 'require("./package.json").version')
* https://github.com/emmanueltouzery/prelude-ts
* (c) 2017-$(git show -s --format=%ai | cut -d - -f 1) Emmanuel Touzery
* prelude-ts may be freely distributed under the ISC license.
*/
END
cat $*
================================================
FILE: src/ChromeDevToolFormatters.ts
================================================
// http://bit.ly/object-formatters
interface ElementHandler {
isElement(object:any): boolean;
getHeader(object:any): any;
hasBody(elt:any): boolean;
getBody(elt:any): any;
}
const olStyle = "list-style-type:none; padding-left: 0px; margin-top: 0px; margin-bottom: 0px; margin-left: 12px";
function getWithToArrayBody(elt: any): any {
return ["ol",
{"style":olStyle},
...elt.toArray().map((x:any,idx:number) => ["li",{},
["span",{"style":"color: rgb(136, 19, 145);"},idx+": "],
["object", {"object":x}]])];
}
class VectorHandler implements ElementHandler {
isElement(object:any): boolean {
return object.hashCode && object.equals &&
object.replace && object.sortOn && object._list;
}
getHeader(object:any): any {
return ["span", {}, "Vector(" + object.length() + ")"];
}
hasBody(elt:any): boolean {
return !elt.isEmpty();
}
getBody = getWithToArrayBody
}
// not going to behave well with infinite streams...
class StreamHandler implements ElementHandler {
isElement(object:any): boolean {
return object.hashCode && object.equals && object.sortBy && object.cycle && object.toVector;
}
getHeader(object:any): any {
// not displaying the length for streams in case
// of infinite streams. the user can expand if needed.
return ["span", {}, "Stream(?)"];
}
hasBody(elt:any): boolean {
return !elt.isEmpty();
}
getBody = getWithToArrayBody
}
class ListHandler implements ElementHandler {
isElement(object:any): boolean {
return object.hashCode && object.equals && object.sortBy && object.toVector;
}
getHeader(object:any): any {
// not displaying the length for streams in case
// of infinite streams. the user can expand if needed.
return ["span", {}, "List(" + object.length() + ")"];
}
hasBody(elt:any): boolean {
return !elt.isEmpty();
}
getBody = getWithToArrayBody
}
class HashSetHandler implements ElementHandler {
isElement(object:any): boolean {
return object.hashCode && object.equals && object.hamt && object.intersect;
}
getHeader(object:any): any {
return ["span", {}, "HashSet(" + object.length() + ")"];
}
hasBody(elt:any): boolean {
return !elt.isEmpty();
}
getBody = getWithToArrayBody
}
class HashMapHandler implements ElementHandler {
isElement(object:any): boolean {
return object.hashCode && object.equals && object.hamt && object.valueIterable;
}
getHeader(object:any): any {
return ["span", {}, "HashMap(" + object.length() + ")"];
}
hasBody(elt:any): boolean {
return !elt.isEmpty();
}
getBody(elt:any): any {
return ["ol",
{"style":olStyle},
...elt.toArray().map((kv:any,idx:number) => {
// using object.create to avoid the __proto__ in the GUI
const obj = Object.create(null);
obj.key = kv[0];
obj.value = kv[1];
return ["li",{},
["span",{"style":"color: rgb(136, 19, 145);"},idx+": "],
["object", {"object":obj}]];
})];
}
}
const handlers = [new VectorHandler(),
new StreamHandler(),
new ListHandler(),
new HashSetHandler(),
new HashMapHandler()];
function getHandler(object: any): ElementHandler|undefined {
return handlers.find(h => h.isElement(object));
}
const formatter = {
header: (object: any, config: any): any => {
const handler = getHandler(object);
return handler ? handler.getHeader(object) : null;
},
hasBody: (object: any, config: any): boolean => {
const handler = getHandler(object);
return handler ? handler.hasBody(object) : false;
},
body: (object: any, config:any): any => {
const handler = getHandler(object);
return handler ? handler.getBody(object) : null;
}
};
if (!(<any>window).devtoolsFormatters) {
(<any>window).devtoolsFormatters = [];
}
(<any>window).devtoolsFormatters.push(formatter);
================================================
FILE: src/Collection.ts
================================================
import { WithEquality, Ordering, ToOrderable } from "./Comparison";
import { Value } from "./Value";
import { Option } from "./Option";
import { HashMap } from "./HashMap";
import { Foldable } from "./Foldable";
export interface Collection<T> extends Value, Iterable<T>, Foldable<T> {
/**
* Get the length of the collection.
*/
length(): number;
/**
* true if the collection is empty, false otherwise.
*/
isEmpty(): boolean;
/**
* Convert to array.
*/
toArray(): Array<T>;
/**
* Call a predicate for each element in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filter<U extends T>(fn:(v:T)=>v is U): Collection<U>;
filter(predicate:(v:T)=>boolean): Collection<T>;
/**
* Returns a pair of two collections; the first one
* will only contain the items from this collection for
* which the predicate you give returns true, the second
* will only contain the items from this collection where
* the predicate returns false.
*
* Vector.of(1,2,3,4).partition(x => x%2===0)
* => [Vector.of(2,4), Vector.of(1,3)]
*/
partition<U extends T>(predicate:(v:T)=>v is U): [Collection<U>,Collection<Exclude<T,U>>];
partition(predicate:(x:T)=>boolean): [Collection<T>,Collection<T>];
/**
* Returns true if the item is in the collection,
* false otherwise.
*/
contains(v:T&WithEquality): boolean;
/**
* Returns true if the predicate returns true for all the
* elements in the collection.
*/
allMatch<U extends T>(predicate:(v:T)=>v is U): this is Collection<U>;
allMatch(predicate:(v:T)=>boolean): boolean;
/**
* Returns true if there the predicate returns true for any
* element in the collection.
*/
anyMatch(predicate:(v:T)=>boolean): boolean;
/**
* If the collection contains a single element,
* return Some of its value, otherwise return None.
*/
single(): Option<T>;
/**
* Group elements in the collection using a classifier function.
* Elements are then organized in a map. The key is the value of
* the classifier, and in value we get the list of elements
* matching that value.
*
* also see [[Collection.arrangeBy]]
*/
groupBy<C>(classifier: (v:T)=>C&WithEquality): HashMap<C,Collection<T>>;
/**
* Matches each element with a unique key that you extract from it.
* If the same key is present twice, the function will return None.
*
* also see [[Collection.groupBy]]
*/
arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>>;
/**
* Compare values in the collection and return the smallest element.
* Returns Option.none if the collection is empty.
*
* also see [[Collection.minOn]]
*/
minBy(compare: (v1:T,v2:T)=>Ordering): Option<T>;
/**
* Call the function you give for each value in the collection
* and return the element for which the result was the smallest.
* Returns Option.none if the collection is empty.
*
* Vector.of({name:"Joe", age:12}, {name:"Paula", age:6}).minOn(x=>x.age)
* => Option.of({name:"Paula", age:6})
*
* also see [[Collection.minBy]]
*/
minOn(getNumber: ToOrderable<T>): Option<T>;
/**
* Compare values in the collection and return the largest element.
* Returns Option.none if the collection is empty.
*
* also see [[Collection.maxOn]]
*/
maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T>;
/**
* Call the function you give for each value in the collection
* and return the element for which the result was the largest.
* Returns Option.none if the collection is empty.
*
* Vector.of({name:"Joe", age:12}, {name:"Paula", age:6}).maxOn(x=>x.age)
* => Option.of({name:"Joe", age:12})
*
* also see [[Collection.maxBy]]
*/
maxOn(getSortable: ToOrderable<T>): Option<T>;
/**
* Call the function you give for each element in the collection
* and sum all the numbers, return that sum.
* Will return 0 if the collection is empty.
*
* Vector.of(1,2,3).sumOn(x=>x)
* => 6
*/
sumOn(getNumber: (v:T)=>number): number;
}
================================================
FILE: src/Comparison.ts
================================================
import { Option } from "./Option";
/**
* Sorting function for type T: function
* to convert this type to a type which is natively
* sortable in javascript, that is string, number or boolean.
* `((v:T)=>number) | ((v:T)=>string) | ((v:T)=>boolean`
*/
export type ToOrderable<T> = ((v:T)=>number) | ((v:T)=>string) | ((v:T)=>boolean);
/**
* List of types which provide equality semantics:
* some builtin JS types, for which === provides
* proper semantics, and then types providing HasEquals.
* The reason I use all over the place T&WithEquality
* instead of saying <T extends WithEquality> earlier
* in the declaration is: https://stackoverflow.com/a/45903143/516188
*/
export type WithEquality
= string
| number
| boolean
| null
| HasEquals;
/**
* A type with semantic equality relationships
*/
export type HasEquals = {equals(other: any): boolean; hashCode(): number;};
/**
* Type guard for HasEquals: find out for a type with
* semantic equality, whether you should call .equals
* or ===
*/
export function hasEquals(v: WithEquality): v is HasEquals {
// there is a reason why we check only for equals, not for hashCode.
// we want to decide which codepath to take: === or equals/hashcode.
// if there is a equals function then we don't want ===, regardless of
// whether there is a hashCode method or not. If there is a equals
// and not hashCode, we want to go on the equals/hashCode codepath,
// which will blow a little later at runtime if the hashCode is missing.
return ((<HasEquals>v).equals !== undefined);
}
/**
* Helper function for your objects so you can compute
* a hashcode. You can pass to this function all the fields
* of your object that should be taken into account for the
* hash, and the function will return a reasonable hash code.
*
* @param fields the fields of your object to take
* into account for the hashcode
*/
export function fieldsHashCode(...fields: any[]): number {
// https://stackoverflow.com/a/113600/516188
// https://stackoverflow.com/a/18066516/516188
let result = 1;
for (const value of fields) {
result = 37*result + getHashCode(value);
}
return result;
}
/**
* Helper function to compute a reasonable hashcode for strings.
*/
export function stringHashCode(str: string): number {
// https://stackoverflow.com/a/7616484/516188
var hash = 0, i, chr;
if (str.length === 0) return hash;
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
/**
* Equality function which tries semantic equality (using .equals())
* if possible, degrades to === if not available, and is also null-safe.
*/
export function areEqual(obj: any|null, obj2: any|null): boolean {
if ((obj === null) != (obj2 === null)) {
return false;
}
if (obj === null || obj2 === null) {
return true;
}
if (hasEquals(obj)) {
return obj.equals(obj2);
}
return obj === obj2;
}
/**
* Hashing function which tries to call hashCode()
* and uses the object itself for numbers, then degrades
* for stringHashCode of the string representation if
* not available.
*/
export function getHashCode(obj: any|null): number {
if (!obj) {
return 0;
}
if (hasEquals(obj)) {
return obj.hashCode();
}
if (typeof obj === 'number') {
// this is the hashcode implementation for numbers from immutablejs
if (obj !== obj || obj === Infinity) {
return 0;
}
let h = obj | 0;
if (h !== obj) {
h ^= obj * 0xffffffff;
}
while (obj > 0xffffffff) {
obj /= 0xffffffff;
h ^= obj;
}
return smi(h);
}
const val = obj+"";
return val.length > STRING_HASH_CACHE_MIN_STRLEN ?
cachedHashString(val) :
stringHashCode(val);
}
function cachedHashString(string: string) {
let hashed = stringHashCache[string];
if (hashed === undefined) {
hashed = stringHashCode(string);
if (STRING_HASH_CACHE_SIZE === STRING_HASH_CACHE_MAX_SIZE) {
STRING_HASH_CACHE_SIZE = 0;
stringHashCache = {};
}
STRING_HASH_CACHE_SIZE++;
stringHashCache[string] = hashed;
}
return hashed;
}
// v8 has an optimization for storing 31-bit signed numbers.
// Values which have either 00 or 11 as the high order bits qualify.
// This function drops the highest order bit in a signed number, maintaining
// the sign bit. (taken from immutablejs)
function smi(i32: number): number {
return ((i32 >>> 1) & 0x40000000) | (i32 & 0xbfffffff);
}
const STRING_HASH_CACHE_MIN_STRLEN = 16;
const STRING_HASH_CACHE_MAX_SIZE = 255;
let STRING_HASH_CACHE_SIZE = 0;
let stringHashCache: {[key:string]:number} = {};
/**
* @hidden
*/
export function hasTrueEquality(val: any): Option<boolean> {
if (!val) {
return Option.none<boolean>();
}
if (val.equals) {
return Option.of(true);
}
switch (val.constructor) {
case String:
case Number:
case Boolean:
return Option.of(true);
}
return Option.of(false);
}
/**
* Enumeration used to express ordering relationships.
* it's a const enum, is replaced by integers in the source.
*/
export const enum Ordering {
/**
* Lower Than
*/
LT=-1,
/**
* EQuals
*/
EQ=0,
/**
* Greater Than
*/
GT=1
};
/**
* Typescript doesn't infer typeguards for lambdas; it only sees
* predicates. This type allows you to cast a predicate to a type
* guard in a handy manner.
*
* It comes in handy for discriminated unions with a 'kind' discriminator,
* for instance:
*
* .`filter(<TypeGuard<InBoard|OutBoard,InBoard>>(p => p.kind === "in_board"))`
*
* Also see [[typeGuard]], [[instanceOf]] and [[typeOf]].
*/
export type TypeGuard<T,U extends T> = (x: T) => x is U;
/**
* Typescript doesn't infer typeguards for lambdas; it only sees
* predicates. This type allows you to cast a predicate to a type
* guard in a handy manner.
*
* It comes in handy for discriminated unions with a 'kind' discriminator,
* for instance:
*
* `.filter(typeGuard(p => p.kind === "in_board", {} as InBoard))`
*
* Normally you'd have to give both type parameters, but you can use
* the type witness parameter as shown in that example to skip
* the first type parameter.
*
* Also see [[typeGuard]], [[instanceOf]] and [[typeOf]].
*/
export function typeGuard<T,U extends T>(predicate:(x:T)=>boolean,
typeWitness?: U): TypeGuard<T,U> {
return <TypeGuard<T,U>>predicate;
}
/**
* Curried function returning a type guard telling us if a value
* is of a specific instance.
* Can be used when filtering to filter for the type and at the
* same time change the type of the generics on the container.
*
* Vector.of<any>("bad", new Date('04 Dec 1995 00:12:00 GMT')).filter(instanceOf(Date))
* => Vector.of<Date>(new Date('04 Dec 1995 00:12:00 GMT'))
*
* Option.of<any>("test").filter(instanceOf(Date))
* => Option.none<Date>()
*
* Option.of<any>(new Date('04 Dec 1995 00:12:00 GMT')).filter(instanceOf(Date))
* => Option.of<Date>(new Date('04 Dec 1995 00:12:00 GMT'))
*
* Also see [[typeGuard]] and [[typeOf]].
*/
export function instanceOf<T>(ctor: new(...args: any[]) => T): TypeGuard<any,T> {
// https://github.com/Microsoft/TypeScript/issues/5101#issuecomment-145693151
return <TypeGuard<any,T>>(x => x instanceof ctor);
}
/**
* Curried function returning a type guard telling us if a value
* is of a specific type.
* Can be used when filtering to filter for the type and at the
* same time change the type of the generics on the container.
*
* Vector.of<any>(1,"a",2,3,"b").filter(typeOf("number"))
* => Vector.of<number>(1,2,3)
*
* Option.of<any>(1).filter(typeOf("string"))
* => Option.none<string>()
*
* Option.of<any>("str").filter(typeOf("string"))
* => Option.of<string>("str")
*
* Also see [[instanceOf]] and [[typeGuard]].
*/
export function typeOf(typ: "string"): TypeGuard<any,string>;
export function typeOf(typ: "number"): TypeGuard<any,number>;
export function typeOf(typ: "boolean"): TypeGuard<any,boolean>;
export function typeOf(typ: "symbol"): TypeGuard<any,symbol>;
export function typeOf(typ: string): TypeGuard<any,any> {
return <any>((x:any) => typeof x === typ);
}
================================================
FILE: src/Contract.ts
================================================
import { hasTrueEquality } from "./Comparison";
import { toStringHelper } from "./SeqHelpers";
let preludeTsContractViolationCb = (msg:string):void => { throw new Error(msg); };
/**
* Some programmatic errors are only detectable at runtime
* (for instance trying to setup a <code>HashSet</code> of <code>Option<number[]></code>: you
* can't reliably compare a <code>number[]</code> therefore you can't compare
* an <code>Option<number[]></code>.. but we can't detect this error at compile-time
* in typescript). So when we detect them at runtime, prelude-ts throws
* an exception by default.
* This function allows you to change that default action
* (for instance, you could display an error message in the console,
* or log the error)
*
* You can reproduce the issue easily by running for instance:
*
* HashSet.of(Option.of([1]))
* => throws
*/
export function setContractViolationAction(action: (msg:string)=>void) {
preludeTsContractViolationCb = action;
}
/**
* @hidden
*/
export function reportContractViolation(msg: string): void {
preludeTsContractViolationCb(msg);
}
/**
* @hidden
*/
export function contractTrueEquality(context: string, ...vals: Array<any>) {
for (const val of vals) {
if (val) {
if (val.hasTrueEquality && (!val.hasTrueEquality())) {
reportContractViolation(
context + ": element doesn't support true equality: " + toStringHelper(val));
}
if (!hasTrueEquality(val).getOrThrow()) {
reportContractViolation(
context + ": element doesn't support equality: " + toStringHelper(val));
}
// the first element i find is looking good, aborting
return;
}
}
}
================================================
FILE: src/Either.ts
================================================
/**
* The [[Either]] type represents an alternative between two value types.
* A "left" value which is also conceptually tied to a failure,
* or a "right" value which is conceptually tied to success.
*
* The code is organized through the class [[Left]], the class [[Right]],
* and the type alias [[Either]] (Left or Right).
*
* Finally, "static" functions on Option are arranged in the class
* [[EitherStatic]] and are accessed through the global constant Either.
*
* Examples:
*
* Either.right<number,number>(5);
* Either.left<number,number>(2);
* Either.right<number,number>(5).map(x => x*2);
*
* Left has the extra [[Left.getLeft]] method that [[Right]] doesn't have.
* Right has the extra [[Right.get]] method that [[Left]] doesn't have.
*/
import { Value, inspect } from "./Value";
import { Option } from "./Option";
import { LinkedList } from "./LinkedList";
import { Vector } from "./Vector";
import { WithEquality, areEqual,
hasTrueEquality, getHashCode } from "./Comparison";
import { contractTrueEquality} from "./Contract";
/**
* Holds the "static methods" for [[Either]]
*/
export class EitherStatic {
/**
* Constructs an Either containing a left value which you give.
*/
left<L,R>(val: L): Either<L,R> {
return new Left<L,R>(val);
}
/**
* Constructs an Either containing a right value which you give.
*/
right<L,R>(val: R): Either<L,R> {
return new Right<L,R>(val);
}
/**
* Curried type guard for Either
* Sometimes needed also due to https://github.com/Microsoft/TypeScript/issues/20218
*
* Vector.of(Either.right<number,number>(2), Either.left<number,number>(1))
* .filter(Either.isLeft)
* .map(o => o.getLeft())
* => Vector.of(1)
*/
isLeft<L,R>(e: Either<L,R>): e is Left<L,R> {
return e.isLeft();
}
/**
* Curried type guard for Either
* Sometimes needed also due to https://github.com/Microsoft/TypeScript/issues/20218
*
* Vector.of(Either.right<number,number>(2), Either.left<number,number>(1))
* .filter(Either.isRight)
* .map(o => o.get())
* => Vector.of(2)
*/
isRight<L,R>(e: Either<L,R>): e is Right<L,R> {
return e.isRight();
}
/**
* Turns a list of eithers in an either containing a list of items.
* Useful in many contexts.
*
* Either.sequence(Vector.of(
* Either.right<number,number>(1),
* Either.right<number,number>(2)));
* => Either.right(Vector.of(1,2))
*
* But if a single element is Left, everything is discarded:
*
* Either.sequence(Vector.of(
* Either.right<number,number>(1),
* Either.left<number,number>(2),
* Either.left<number,number>(3)));
* => Either.left(2)
*
* Also see [[EitherStatic.traverse]]
*/
sequence<L,R>(elts:Iterable<Either<L,R>>): Either<L,Vector<R>> {
return Either.traverse(elts, x=>x);
}
/**
* Takes a list, a function that can transform list elements
* to eithers, then return an either containing a list of
* the transformed elements.
*
* const getUserById: (x:number)=>Either<string,string> = x => x > 0 ?
* Either.right("user" + x.toString()) : Either.left("invalid id!");
* Either.traverse([4, 3, 2], getUserById);
* => Either.right(Vector.of("user4", "user3", "user2"))
*
* But if a single element results in Left, everything is discarded:
*
* const getUserById: (x:number)=>Either<string,string> = x => x > 0 ?
* Either.right("user" + x.toString()) : Either.left("invalid id!");
* Either.traverse([4, -3, 2], getUserById);
* => Either.left("invalid id!")
*
* Also see [[EitherStatic.sequence]]
*/
traverse<T,L,R>(elts:Iterable<T>, fn: (x:T)=>Either<L,R>): Either<L,Vector<R>> {
let r = Vector.empty<R>();
const iterator = elts[Symbol.iterator]();
let curItem = iterator.next();
while (!curItem.done) {
const v = fn(curItem.value);
if (v.isLeft()) {
return <any>v;
}
r = r.append(v.get());
curItem = iterator.next();
}
return Either.right<L,Vector<R>>(r);
}
/**
* Turns a list of eithers in an either containing a list of items.
* Compared to [[EitherStatic.sequence]], sequenceAcc 'accumulates'
* the errors, instead of short-circuiting on the first error.
*
* Either.sequenceAcc(Vector.of(
* Either.right<number,number>(1),
* Either.right<number,number>(2)));
* => Either.right(Vector.of(1,2))
*
* But if a single element is Left, you get all the lefts:
*
* Either.sequenceAcc(Vector.of(
* Either.right<number,number>(1),
* Either.left<number,number>(2),
* Either.left<number,number>(3)));
* => Either.left(Vector.of(2,3))
*/
sequenceAcc<L,R>(elts:Iterable<Either<L,R>>): Either<Vector<L>,Vector<R>> {
const [lefts,rights] = Vector.ofIterable(elts).partition(Either.isLeft);
if (lefts.isEmpty()) {
return Either.right<Vector<L>,Vector<R>>(rights.map(r => r.getOrThrow()));
}
return Either.left<Vector<L>,Vector<R>>(lefts.map(l => l.getLeft()));
}
/**
* Applicative lifting for Either.
* Takes a function which operates on basic values, and turns it
* in a function that operates on eithers of these values ('lifts'
* the function). The 2 is because it works on functions taking two
* parameters.
*
* const lifted = Either.liftA2(
* (x:number,y:number) => x+y, {} as string);
* lifted(
* Either.right<string,number>(5),
* Either.right<string,number>(6));
* => Either.right(11)
*
* const lifted = Either.liftA2(
* (x:number,y:number) => x+y, {} as string);
* lifted(
* Either.right<string,number>(5),
* Either.left<string,number>("bad"));
* => Either.left("bad")
*
* @param R1 the first right type
* @param R2 the second right type
* @param L the left type
* @param V the new right type as returned by the combining function.
*/
liftA2<R1,R2,L,V>(fn:(v1:R1,v2:R2)=>V, leftWitness?: L) : (p1:Either<L,R1>, p2:Either<L,R2>) => Either<L,V> {
return (p1,p2) => p1.flatMap(a1 => p2.map(a2 => fn(a1,a2)));
}
/**
* Applicative lifting for Either. 'p' stands for 'properties'.
*
* Takes a function which operates on a simple JS object, and turns it
* in a function that operates on the same JS object type except which each field
* wrapped in an Either ('lifts' the function).
* It's an alternative to [[EitherStatic.liftA2]] when the number of parameters
* is not two.
*
* const fn = (x:{a:number,b:number,c:number}) => x.a+x.b+x.c;
* const lifted = Either.liftAp(fn, {} as number);
* lifted({
* a: Either.right<number,number>(5),
* b: Either.right<number,number>(6),
* c: Either.right<number,number>(3)});
* => Either.right(14)
*
* const lifted = Either.liftAp<number,{a:number,b:number},number>(
* x => x.a+x.b);
* lifted({
* a: Either.right<number,number>(5),
* b: Either.left<number,number>(2)});
* => Either.left(2)
*
* @param L the left type
* @param A the object property type specifying the parameters for your function
* @param B the type returned by your function, returned wrapped in an either by liftAp.
*/
liftAp<L,A,B>(fn:(x:A)=>B, leftWitness?: L): (x: {[K in keyof A]: Either<L,A[K]>;}) => Either<L,B> {
return x => {
const copy:A = <any>{};
for (let p in x) {
if (x[p].isLeft()) {
return <Either<L,B>><any>x[p];
}
copy[p] = x[p].getOrThrow();
}
return Either.right<L,B>(fn(copy));
}
}
/**
* Applicative lifting for Either. 'p' stands for 'properties'.
* Compared to [[EitherStatic.liftAp]], liftApAcc 'accumulates'
* the errors, instead of short-circuiting on the first error.
*
* Takes a function which operates on a simple JS object, and turns it
* in a function that operates on the same JS object type except which each field
* wrapped in an Either ('lifts' the function).
* It's an alternative to [[EitherStatic.liftA2]] when the number of parameters
* is not two.
*
* const fn = (x:{a:number,b:number,c:number}) => x.a+x.b+x.c;
* const lifted = Either.liftApAcc(fn, {} as number);
* lifted({
* a: Either.right<number,number>(5),
* b: Either.right<number,number>(6),
* c:Either.right<number,number>(3)});
* => Either.right(14)
*
* const fn = (x:{a:number,b:number,c:number}) => x.a+x.b+x.c;
* const lifted = Either.liftApAcc(fn, {} as number);
* lifted({
* a: Either.right<number,number>(5),
* b: Either.left<number,number>(2),
* c: Either.left<number,number>(6)});
* => Either.left(Vector.of(2, 6))
*
* @param L the left type
* @param A the object property type specifying the parameters for your function
* @param B the type returned by your function, returned wrapped in an either by liftAp.
*/
liftApAcc<L,A,B>(fn:(x:A)=>B, leftWitness?: L): (x: {[K in keyof A]: Either<L,A[K]>;}) => Either<Vector<L>,B> {
const leftErrs: L[] = [];
return x => {
const copy:A = <any>{};
for (let p in x) {
const field = x[p];
if (field.isLeft()) {
leftErrs.push(field.getLeft());
} else {
copy[p] = x[p].getOrThrow();
}
}
if (leftErrs.length === 0) {
return Either.right<Vector<L>,B>(fn(copy));
} else {
return Either.left<Vector<L>,B>(Vector.ofIterable(leftErrs));
}
}
}
/**
* Take a partial function (may return undefined or throw),
* and lift it to return an [[Either]] instead.
*
* Note that unlike the [[OptionStatic.lift]] version, if
* the function returns undefined, the Either.lift version will throw
* (the Option.lift version returns None()): if you want to do
* pure side-effects which may throw, you're better off just using
* javascript try blocks.
*
* When using typescript, to help the compiler infer the left type,
* you can either pass a second parameter like `{} as <type>`, or
* call with `lift<L,R>(...)`.
*
* const add = Either.lift((x:number,y:number) => x+y, {} as string);
* add(1,2);
* => Either.right(3)
*
* const undef = Either.lift((x:number,y:number,z:number) => undefined);
* undef(1,2,3);
* => throws
*
* const throws = Either.lift(() => {throw "x"});
* throws();
* => Either.left("x")
*/
lift<T extends any[],L,U>(fn: (...args: T)=>U, witness?: L): (...args:T)=>Either<L,U> {
return (...args:T) => {
try {
const r = fn(...args);
if (r !== undefined) {
return Either.right(r);
}
} catch (err) {
return Either.left(err);
}
throw new Error("liftEither got undefined!");
};
}
/**
* Take a no-parameter partial function (may return undefined or throw),
* call it, and return an [[Either]] instead.
*
* Note that unlike the [[OptionStatic.try_]] version, if
* the function returns undefined, this function will throw
* (the Option.try_ version returns None()): if you want to do
* pure side-effects which may throw, you're better off just using
* javascript try blocks.
*
* When using typescript, to help the compiler infer the left type,
* you can either pass a second parameter like `{} as <type>`, or
* call with `try_<L,R>(...)`.
*
* Either.try_(Math.random, {} as string);
* => Either.right(0.49884723907769635)
*
* Either.try_(() => undefined);
* => throws
*
* Either.try_(() => {throw "x"});
* => Either.left("x")
*
* Also see [[EitherStatic.lift]], [[OptionStatic.try_]],
* [[OptionStatic.tryNullable]]
*/
try_<L,T>(fn:()=>T, witness?: L): Either<L,T> {
return Either.lift<[],L,T>(fn)();
}
}
/**
* The Either constant allows to call the either "static" methods
*/
export const Either = new EitherStatic();
/**
* Either represents an alternative between two value types.
* A "left" value which is also conceptually tied to a failure,
* or a "right" value which is conceptually tied to success.
* "static methods" available through [[EitherStatic]]
*/
export type Either<L,R> = Left<L,R> | Right<L,R>;
/**
* Represents an [[Either]] containing a left value,
* conceptually tied to a failure.
* "static methods" available through [[EitherStatic]]
* @param L the "left" item type 'failure'
* @param R the "right" item type 'success'
*/
export class Left<L,R> implements Value {
constructor(private value: L) {}
/**
* @hidden
*/
readonly className: "Left" = <any>undefined; // https://stackoverflow.com/a/47841595/516188
/**
* Returns true since this is a Left
*/
isLeft(): this is Left<L,R> {
return true;
}
/**
* Returns false since this is a Left
*/
isRight(): this is Right<L,R> {
return false;
}
/**
* Returns true if this is either is a right and contains the value you give.
*/
contains(val: R&WithEquality): boolean {
return false;
}
/**
* If this either is a right, applies the function you give
* to its contents and build a new right either, otherwise return this.
*/
map<U>(fn: (x:R)=>U): Either<L,U> {
return <any>this;
}
/**
* If this either is a right, call the function you give with
* the contents, and return what the function returns, else
* returns this.
* This is the monadic bind.
*/
flatMap<U>(fn: (x:R)=>Either<L,U>): Either<L,U> {
return <any>this;
}
/**
* If this either is a left, call the function you give with
* the left value and return a new either left with the result
* of the function, else return this.
*/
mapLeft<U>(fn: (x:L)=>U): Either<U,R> {
return new Left<U,R>(fn(this.value));
}
/**
* Map the either: you give a function to apply to the value,
* a function in case it's a left, a function in case it's a right.
*/
bimap<S,T>(fnL: (x:L)=>S,fnR: (x:R)=>T): Either<S,T> {
return new Left<S,T>(fnL(this.value));
}
/**
* "filter" the either. If it was a Left, it stays a Left.
* If it was a Right and the predicate you pass returns
* true for its value, return the either unchanged.
* But if it was a left and the predicate returns false,
* return a Left with the value returned by the function
* passed as second parameter.
*
* Either.right<string,number>(-3)
* .filter(x => x >= 0, v => "got negative value: " + v);
* => Either.left<string,number>("got negative value: -3")
*/
filter(p: (x:R)=>boolean, filterVal: (x:R)=>L): Either<L,R> {
return this;
}
/**
* Combines two eithers. If this either is a right, returns it.
* If it's a left, returns the other one.
*/
orElse(other: Either<L,R>): Either<L,R> {
return other;
}
/**
* Has no effect if this Either is a right. If it's a left however,
* the function you give will be called, receiving as parameter
* the left contents, and an Either equivalent to the one your
* function returns will be returned.
*/
recoverWith(recoveryFn: (left:L)=>Either<L, R>): Either<L, R> {
return recoveryFn(this.value);
}
/**
* Execute a side-effecting function if the either
* is a right; returns the either.
*/
ifRight(fn: (x:R)=>void): Either<L,R> {
return this;
}
/**
* Execute a side-effecting function if the either
* is a left; returns the either.
*/
ifLeft(fn: (x:L)=>void): Either<L,R> {
fn(this.value);
return this;
}
/**
* Handle both branches of the either and return a value
* (can also be used for side-effects).
* This is the catamorphism for either.
*
* Either.right<string,number>(5).match({
* Left: x => "left " + x,
* Right: x => "right " + x
* });
* => "right 5"
*/
match<U>(cases: {Left: (v:L)=>U, Right: (v:R)=>U}): U {
return cases.Left(this.value);
}
/**
* If this either is a right, return its value, else throw
* an exception.
* You can optionally pass a message that'll be used as the
* exception message, or an Error object.
*/
getOrThrow(errorInfo?: Error|string): R {
if (typeof errorInfo === 'string') {
throw new Error(errorInfo || "Left.getOrThrow called!");
}
throw errorInfo || new Error("Left.getOrThrow called!");
}
/**
* If this either is a right, return its value, else return
* the value you give.
*/
getOrElse(other: R): R {
return other;
}
/**
* Get the value contained in this left.
* NOTE: we know it's there, since this method
* belongs to Left, not Either.
*/
getLeft(): L {
return this.value;
}
/**
* If this either is a left, return its value, else throw
* an exception.
* You can optionally pass a message that'll be used as the
* exception message.
*/
getLeftOrThrow(message?: string): L {
return this.value;
}
/**
* If this either is a left, return its value, else return
* the value you give.
*/
getLeftOrElse(other: L): L {
return this.value;
}
/**
* Convert this either to an option, conceptually dropping
* the left (failing) value.
*/
toOption(): Option<R> {
return Option.none<R>();
}
/**
* Convert to a vector. If it's a left, it's the empty
* vector, if it's a right, it's a one-element vector with
* the contents of the either.
*/
toVector(): Vector<R> {
return Vector.empty<R>();
}
/**
* Convert to a list. If it's a left, it's the empty
* list, if it's a right, it's a one-element list with
* the contents of the either.
*/
toLinkedList(): LinkedList<R> {
return LinkedList.empty<R>();
}
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:Either<L,R>)=>U): U {
return converter(this);
}
hasTrueEquality(): boolean {
return (this.value && (<any>this.value).hasTrueEquality) ?
(<any>this.value).hasTrueEquality() :
hasTrueEquality(this.value);
}
/**
* Get a number for that object. Two different values
* may get the same number, but one value must always get
* the same number. The formula can impact performance.
*/
hashCode(): number {
return getHashCode(this.value);
}
/**
* Two objects are equal if they represent the same value,
* regardless of whether they are the same object physically
* in memory.
*/
equals(other: Either<L&WithEquality,R&WithEquality>): boolean {
if (<any>other === this) {
return true;
}
if ((!other) || (!other.isRight) || other.isRight()) {
return false;
}
const leftOther = <Left<L&WithEquality,R&WithEquality>>other;
contractTrueEquality("Either.equals", this, leftOther);
return areEqual(this.value, leftOther.value);
}
/**
* Get a human-friendly string representation of that value.
*/
toString(): string {
return "Left(" + this.value + ")";
}
/**
* Used by the node REPL to display values.
*/
[inspect](): string {
return this.toString();
}
}
/**
* Represents an [[Either]] containing a success value,
* conceptually tied to a success.
* "static methods" available through [[EitherStatic]]
* @param L the "left" item type 'failure'
* @param R the "right" item type 'success'
*/
export class Right<L,R> implements Value {
constructor(private value: R) {}
/**
* @hidden
*/
readonly className: "Right" = <any>undefined; // https://stackoverflow.com/a/47841595/516188
/**
* Returns false since this is a Right
*/
isLeft(): this is Left<L,R> {
return false;
}
/**
* Returns true since this is a Right
*/
isRight(): this is Right<L,R> {
return true;
}
/**
* Returns true if this is either is a right and contains the value you give.
*/
contains(val: R&WithEquality): boolean {
return areEqual(this.value, val);
}
/**
* If this either is a right, applies the function you give
* to its contents and build a new right either, otherwise return this.
*/
map<U>(fn: (x:R)=>U): Either<L,U> {
return new Right<L,U>(fn(this.value));
}
/**
* If this either is a right, call the function you give with
* the contents, and return what the function returns, else
* returns this.
* This is the monadic bind.
*/
flatMap<U>(fn: (x:R)=>Either<L,U>): Either<L,U> {
return fn(this.value);
}
/**
* If this either is a left, call the function you give with
* the left value and return a new either left with the result
* of the function, else return this.
*/
mapLeft<U>(fn: (x:L)=>U): Either<U,R> {
return <any>this;
}
/**
* Map the either: you give a function to apply to the value,
* a function in case it's a left, a function in case it's a right.
*/
bimap<S,T>(fnL: (x:L)=>S,fnR: (x:R)=>T): Either<S,T> {
return new Right<S,T>(fnR(this.value));
}
/**
* "filter" the either. If it was a Left, it stays a Left.
* If it was a Right and the predicate you pass returns
* true for its value, return the either unchanged.
* But if it was a left and the predicate returns false,
* return a Left with the value returned by the function
* passed as second parameter.
*
* Either.right<string,number>(-3)
* .filter(x => x >= 0, v => "got negative value: " + v);
* => Either.left<string,number>("got negative value: -3")
*/
filter(p: (x:R)=>boolean, filterVal: (x:R)=>L): Either<L,R> {
if (p(this.value)) {
return this;
}
return new Left(filterVal(this.value));
}
/**
* Combines two eithers. If this either is a right, returns it.
* If it's a left, returns the other one.
*/
orElse(other: Either<L,R>): Either<L,R> {
return this;
}
/**
* Has no effect if this Either is a right. If it's a left however,
* the function you give will be called, receiving as parameter
* the left contents, and an Either equivalent to the one your
* function returns will be returned.
*/
recoverWith(recoveryFn: (left:L)=>Either<L, R>): Either<L, R> {
return this;
}
/**
* Execute a side-effecting function if the either
* is a right; returns the either.
*/
ifRight(fn: (x:R)=>void): Either<L,R> {
fn(this.value);
return this;
}
/**
* Execute a side-effecting function if the either
* is a left; returns the either.
*/
ifLeft(fn: (x:L)=>void): Either<L,R> {
return this;
}
/**
* Handle both branches of the either and return a value
* (can also be used for side-effects).
* This is the catamorphism for either.
*
* Either.right<string,number>(5).match({
* Left: x => "left " + x,
* Right: x => "right " + x
* });
* => "right 5"
*/
match<U>(cases: {Left: (v:L)=>U, Right: (v:R)=>U}): U {
return cases.Right(this.value);
}
/**
* Get the value contained in this right.
* NOTE: we know it's there, since this method
* belongs to Right, not Either.
*/
get(): R {
return this.value;
}
/**
* If this either is a right, return its value, else throw
* an exception.
* You can optionally pass a message that'll be used as the
* exception message, or an Error object.
*/
getOrThrow(errorInfo?: Error|string): R {
return this.value;
}
/**
* If this either is a right, return its value, else return
* the value you give.
*/
getOrElse(other: R): R {
return this.value;
}
/**
* If this either is a left, return its value, else throw
* an exception.
* You can optionally pass a message that'll be used as the
* exception message.
*/
getLeftOrThrow(message?: string): L {
throw message || "Left.getOrThrow called!";
}
/**
* If this either is a left, return its value, else return
* the value you give.
*/
getLeftOrElse(other: L): L {
return other;
}
/**
* Convert this either to an option, conceptually dropping
* the left (failing) value.
*/
toOption(): Option<R> {
return Option.of(this.value);
}
/**
* Convert to a vector. If it's a left, it's the empty
* vector, if it's a right, it's a one-element vector with
* the contents of the either.
*/
toVector(): Vector<R> {
return Vector.of(this.value);
}
/**
* Convert to a list. If it's a left, it's the empty
* list, if it's a right, it's a one-element list with
* the contents of the either.
*/
toLinkedList(): LinkedList<R> {
return LinkedList.of(this.value);
}
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:Either<L,R>)=>U): U {
return converter(this);
}
hasTrueEquality(): boolean {
return (this.value && (<any>this.value).hasTrueEquality) ?
(<any>this.value).hasTrueEquality() :
hasTrueEquality(this.value);
}
/**
* Get a number for that object. Two different values
* may get the same number, but one value must always get
* the same number. The formula can impact performance.
*/
hashCode(): number {
return getHashCode(this.value);
}
/**
* Two objects are equal if they represent the same value,
* regardless of whether they are the same object physically
* in memory.
*/
equals(other: Either<L&WithEquality,R&WithEquality>): boolean {
if (<any>other === this) {
return true;
}
if ((!other) || (!other.isRight) || (!other.isRight())) {
return false;
}
const rightOther = <Right<L&WithEquality,R&WithEquality>>other;
contractTrueEquality("Either.equals", this, rightOther);
return areEqual(this.value, rightOther.value);
}
/**
* Get a human-friendly string representation of that value.
*/
toString(): string {
return "Right(" + this.value + ")";
}
/**
* Used by the node REPL to display values.
*/
[inspect](): string {
return this.toString();
}
}
================================================
FILE: src/Foldable.ts
================================================
import { Option } from "./Option";
export interface Foldable<T> {
/**
* Reduces the collection to a single value using the
* associative binary function you give. Since the function
* is associative, order of application doesn't matter.
*
* Example:
*
* HashSet.of(1,2,3).fold(0, (a,b) => a + b);
* => 6
*/
fold(zero:T, fn:(v1:T,v2:T)=>T): T;
/**
* Reduces the collection to a single value.
* Left-associative.
*
* Example:
*
* Vector.of("a", "b", "c").foldLeft("!", (xs,x) => x+xs);
* => "cba!"
*
* @param zero The initial value
* @param fn A function taking the previous value and
* the current collection item, and returning
* an updated value.
*/
foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U;
/**
* Reduces the collection to a single value.
* Right-associative.
*
* Example:
*
* Vector.of("a", "b", "c").foldRight("!", (x,xs) => xs+x)
* => "!cba"
*
* @param zero The initial value
* @param fn A function taking the current collection item and
* the previous value , and returning
* an updated value.
*/
foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U;
/**
* Reduces the collection to a single value by repeatedly
* calling the combine function.
* No starting value. The order in which the elements are
* passed to the combining function is undetermined.
*/
reduce(combine: (v1:T,v2:T)=>T): Option<T>;
}
================================================
FILE: src/Function.ts
================================================
/**
* Rich functions with helpers such as [[Function1.andThen]],
* [[Function2.apply1]] and so on.
*
* We support functions of arities up to 5. For each arity, we have
* the interface ([[Function1]], [[Function2]], ...), builders are on functions
* on [[Function1Static]], [[Function2Static]]... accessible on constants
* named Function1, Function2,...
*
* Examples:
*
* const combined = Function1.of((x:number)=>x+2).andThen(x=>x*3);
* combined(6);
* => 24
*
* const plus5 = Function2.of((x:number,y:number)=>x+y).apply1(5);
* plus5(1);
* => 6
*/
/**
* Function0 encapsulates a parameterless function
* which returns a value. It adds some useful functions
* to combine or transform functions.
*
* @param T the parameter type
* @param U the result type
*/
export interface Function0<R> {
/**
* Invoke the function
*/
(): R;
/**
* Returns a new composed function which first calls the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:R)=>V): Function0<V>;
}
/**
* Function1 encapsulates a function taking a single parameter
* and returning a value. It adds some useful functions
* to combine or transform functions.
*
* @param T the parameter type
* @param U the result type
*/
export interface Function1<T,U> {
/**
* Invoke the function
*/
(x:T): U;
/**
* Returns a new composed function which first applies the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:U)=>V): Function1<T,V>;
/**
*
*/
compose<S>(fn:(x:S)=>T): Function1<S,U>;
}
/**
* Function2 encapsulates a function taking two parameters
* and returning a value. It adds some useful functions
* to combine or transform functions.
*
* @param T1 the first parameter type
* @param T2 the second parameter type
* @param R the result type
*/
export interface Function2<T1,T2,R> {
/**
* Invoke the function
*/
(x:T1,y:T2): R;
/**
* Returns a new composed function which first applies the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:R)=>V): Function2<T1,T2,V>;
/**
* Returns a curried version of this function, for example:
*
* const plus5 = Function2.of(
* (x:number,y:number)=>x+y)
* .curried()(5);
* assert.equal(6, plus5(1));
*/
curried(): Function1<T1,Function1<T2,R>>;
/**
* Returns a version of this function which takes a tuple
* instead of individual parameters. Useful in combination
* with [[Vector.zip]] for instance.
*/
tupled(): Function1<[T1,T2],R>;
/**
* Returns a version of this function taking its parameters
* in the reverse order.
*/
flipped(): Function2<T2,T1,R>;
/**
* Applies this function partially to one argument.
*
* const plus5 = Function2.of(
* (x:number,y:number)=>x+y)
* .apply1(5);
* assert.equal(6, plus5(1));
*/
apply1(param1:T1): Function1<T2,R>;
}
/**
* Function3 encapsulates a function taking three parameters
* and returning a value. It adds some useful functions
* to combine or transform functions.
*
* @param T1 the first parameter type
* @param T2 the second parameter type
* @param T3 the third parameter type
* @param R the result type
*/
export interface Function3<T1,T2,T3,R> {
/**
* Invoke the function
*/
(x:T1,y:T2,z:T3): R;
/**
* Returns a new composed function which first applies the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:R)=>V): Function3<T1,T2,T3,V>;
/**
* Returns a curried version of this function, for example:
* See [[Function2.curried]]
*/
curried(): Function1<T1,Function1<T2,Function1<T3,R>>>;
/**
* Returns a version of this function which takes a tuple
* instead of individual parameters.
*/
tupled(): Function1<[T1,T2,T3],R>;
/**
* Returns a version of this function taking its parameters
* in the reverse order.
*/
flipped(): Function3<T3,T2,T1,R>;
/**
* Applies this function partially to one argument.
*
* const plus5 = Function3.of(
* (x:number,y:number,z:number)=>x+y+z)
* .apply1(5);
* assert.equal(8, plus5(1,2));
*/
apply1(param1:T1): Function2<T2,T3,R>;
/**
* Applies this function partially to two arguments.
*
* const plus54 = Function3.of(
* (x:number,y:number,z:number)=>x+y+z)
* .apply2(5,4);
* assert.equal(12, plus54(3));
*/
apply2(param1:T1, param2: T2): Function1<T3,R>;
}
/**
* Function4 encapsulates a function taking four parameters
* and returning a value. It adds some useful functions
* to combine or transform functions.
*
* @param T1 the first parameter type
* @param T2 the second parameter type
* @param T3 the third parameter type
* @param T4 the fourth parameter type
* @param R the result type
*/
export interface Function4<T1,T2,T3,T4,R> {
/**
* Invoke the function
*/
(x:T1,y:T2,z:T3,a:T4): R;
/**
* Returns a new composed function which first applies the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:R)=>V): Function4<T1,T2,T3,T4,V>;
/**
* Returns a curried version of this function, for example:
* See [[Function2.curried]]
*/
curried(): Function1<T1,Function1<T2,Function1<T3,Function1<T4,R>>>>;
/**
* Returns a version of this function which takes a tuple
* instead of individual parameters.
*/
tupled(): Function1<[T1,T2,T3,T4],R>;
/**
* Returns a version of this function taking its parameters
* in the reverse order.
*/
flipped(): Function4<T4,T3,T2,T1,R>;
/**
* Applies this function partially to one argument.
*
* const plus5 = Function4.of(
* (x:number,y:number,z:number,a:number)=>x+y+z+a)
* .apply1(5);
* assert.equal(11, plus5(1,2,3));
*/
apply1(param1:T1): Function3<T2,T3,T4,R>;
/**
* Applies this function partially to two arguments.
*
* const plus51 = Function4.of(
* (x:number,y:number,z:number,a:number)=>x+y+z+a)
* .apply2(5,1);
* assert.equal(11, plus51(2,3));
*/
apply2(param1:T1, param2: T2): Function2<T3,T4,R>;
/**
* Applies this function partially to three arguments.
*
* const plus512 = Function4.of(
* (x:number,y:number,z:number,a:number)=>x+y+z+a)
* .apply3(5,1,2);
* assert.equal(11, plus512(3));
*/
apply3(param1:T1, param2: T2, param3: T3): Function1<T4,R>;
}
/**
* Function5 encapsulates a function taking give parameters
* and returning a value. It adds some useful functions
* to combine or transform functions.
*
* @param T1 the first parameter type
* @param T2 the second parameter type
* @param T3 the third parameter type
* @param T4 the fourth parameter type
* @param T5 the fifth parameter type
* @param R the result type
*/
export interface Function5<T1,T2,T3,T4,T5,R> {
/**
* Invoke the function
*/
(x:T1,y:T2,z:T3,a:T4,b:T5): R;
/**
* Returns a new composed function which first applies the current
* function and then the one you pass as parameter.
*/
andThen<V>(fn:(x:R)=>V): Function5<T1,T2,T3,T4,T5,V>;
/**
* Returns a curried version of this function, for example:
* See [[Function2.curried]]
*/
curried(): Function1<T1,Function1<T2,Function1<T3,Function1<T4,Function1<T5,R>>>>>;
/**
* Returns a version of this function which takes a tuple
* instead of individual parameters.
*/
tupled(): Function1<[T1,T2,T3,T4,T5],R>;
/**
* Returns a version of this function taking its parameters
* in the reverse order.
*/
flipped(): Function5<T5,T4,T3,T2,T1,R>;
/**
* Applies this function partially to one argument.
*
* const plus5 = Function5.of(
* (x:number,y:number,z:number,a:number,b:number)=>x+y+z+a+b)
* .apply1(5);
* assert.equal(15, plus5(1,2,3,4));
*/
apply1(param1:T1): Function4<T2,T3,T4,T5,R>;
/**
* Applies this function partially to two arguments.
*
* const plus51 = Function5.of(
* (x:number,y:number,z:number,a:number,b:number)=>x+y+z+a+b)
* .apply2(5,1);
* assert.equal(15, plus51(2,3,4));
*/
apply2(param1:T1, param2: T2): Function3<T3,T4,T5,R>;
/**
* Applies this function partially to three arguments.
*
* const plus512 = Function5.of(
* (x:number,y:number,z:number,a:number,b:number)=>x+y+z+a+b)
* .apply3(5,1,2);
* assert.equal(15, plus512(3,4));
*/
apply3(param1:T1, param2: T2, param3: T3): Function2<T4,T5,R>;
/**
* Applies this function partially to four arguments.
*
* const plus5123 = Function5.of(
* (x:number,y:number,z:number,a:number,b:number)=>x+y+z+a+b)
* .apply4(5,1,2,3);
* assert.equal(15, plus5123(4));
*/
apply4(param1:T1, param2: T2, param3: T3, param4: T4): Function1<T5,R>;
}
/**
* This is the type of the Function0 constant, which
* offers some helper functions to deal
* with [[Function0]] including
* the ability to build [[Function0]]
* from functions using [[Function0Static.of]].
* It also offers some builtin functions like [[Function0Static.constant]].
*/
export class Function0Static {
/**
* The constant function of one parameter:
* will always return the value you give, no
* matter the parameter it's given.
*/
constant<R>(val:R): Function0<R> {
return Function0.of(()=>val);
}
/**
* Take a one-parameter function and lift it to become a [[Function1Static]],
* enabling you to call [[Function1.andThen]] and other such methods on it.
*/
of<R>(fn:()=>R): Function0<R> {
const r = <Function0<R>>(() => fn());
r.andThen = <V>(fn2:(x:R)=>V) => Function0.of(() => fn2(r()));
return r;
}
}
/**
* The Function1 constant allows to call the [[Function0]] "static" methods.
*/
export const Function0 = new Function0Static();
/**
* This is the type of the Function1 constant, which
* offers some helper functions to deal
* with [[Function1]] including
* the ability to build [[Function1]]
* from functions using [[Function1Static.of]].
* It also offers some builtin functions like [[Function1Static.constant]].
*/
export class Function1Static {
/**
* The identity function.
*/
id<T>(): Function1<T,T> {
return Function1.of((x:T)=>x);
}
/**
* The constant function of one parameter:
* will always return the value you give, no
* matter the parameter it's given.
*/
constant<U,T>(val:T): Function1<U,T> {
return Function1.of((x:U)=>val);
}
/**
* Take a one-parameter function and lift it to become a [[Function1Static]],
* enabling you to call [[Function1.andThen]] and other such methods on it.
*/
of<T,U>(fn:(x:T)=>U): Function1<T,U> {
const r = <Function1<T,U>>(x => fn(x));
r.andThen = <V>(fn2:(x:U)=>V) => Function1.of((x:T) => fn2(r(x)));
r.compose = <S>(fn2:(x:S)=>T) => Function1.of((x:S) => r(fn2(x)));
return r;
}
}
/**
* The Function1 constant allows to call the [[Function1]] "static" methods.
*/
export const Function1 = new Function1Static();
/**
* This is the type of the Function2 constant, which
* offers some helper functions to deal
* with [[Function2]] including
* the ability to build [[Function2]]
* from functions using [[Function2Static.of]].
* It also offers some builtin functions like [[Function2Static.constant]].
*/
export class Function2Static {
/**
* The constant function of two parameters:
* will always return the value you give, no
* matter the parameters it's given.
*/
constant<T1,T2,R>(val:R): Function2<T1,T2,R> {
return Function2.of((x:T1,y:T2)=>val);
}
/**
* Take a two-parameter function and lift it to become a [[Function2]],
* enabling you to call [[Function2.andThen]] and other such methods on it.
*/
of<T1,T2,R>(fn:(x:T1,y:T2)=>R): Function2<T1,T2,R> {
const r = <Function2<T1,T2,R>>((x,y)=>fn(x,y));
r.andThen = <V>(fn2:(x:R)=>V) => Function2.of((x:T1,y:T2) => fn2(r(x,y)));
r.curried = () => Function1.of((x:T1) => Function1.of((y:T2) => r(x,y)));
r.tupled = () => Function1.of((pair:[T1,T2]) => r(pair[0],pair[1]));
r.flipped = () => Function2.of((x:T2,y:T1) => r(y,x));
r.apply1 = (x:T1) => Function1.of((y:T2) => r(x,y));
return r;
}
}
/**
* The Function2 constant allows to call the [[Function2]] "static" methods.
*/
export const Function2 = new Function2Static();
/**
* This is the type of the Function3 constant, which
* offers some helper functions to deal
* with [[Function3]] including
* the ability to build [[Function3]]
* from functions using [[Function3Static.of]].
* It also offers some builtin functions like [[Function3Static.constant]].
*/
export class Function3Static {
/**
* The constant function of three parameters:
* will always return the value you give, no
* matter the parameters it's given.
*/
constant<T1,T2,T3,R>(val:R): Function3<T1,T2,T3,R> {
return Function3.of((x:T1,y:T2,z:T3)=>val);
}
/**
* Take a three-parameter function and lift it to become a [[Function3]],
* enabling you to call [[Function3.andThen]] and other such methods on it.
*/
of<T1,T2,T3,R>(fn:(x:T1,y:T2,z:T3)=>R): Function3<T1,T2,T3,R> {
const r = <Function3<T1,T2,T3,R>>((x,y,z)=>fn(x,y,z));
r.andThen = <V>(fn2:(x:R)=>V) => Function3.of((x:T1,y:T2,z:T3) => fn2(r(x,y,z)));
r.curried = () => Function1.of((x:T1) => Function1.of((y:T2) => Function1.of((z:T3) => r(x,y,z))));
r.tupled = () => Function1.of((tuple:[T1,T2,T3]) => r(tuple[0],tuple[1],tuple[2]));
r.flipped = () => Function3.of((x:T3,y:T2,z:T1) => r(z,y,x));
r.apply1 = (x:T1) => Function2.of((y:T2,z:T3) => r(x,y,z));
r.apply2 = (x:T1,y:T2) => Function1.of((z:T3) => r(x,y,z));
return r;
}
}
/**
* The Function3 constant allows to call the [[Function3]] "static" methods.
*/
export const Function3 = new Function3Static();
/**
* This is the type of the Function4 constant, which
* offers some helper functions to deal
* with [[Function4]] including
* the ability to build [[Function4]]
* from functions using [[Function4Static.of]].
* It also offers some builtin functions like [[Function4Static.constant]].
*/
export class Function4Static {
/**
* The constant function of four parameters:
* will always return the value you give, no
* matter the parameters it's given.
*/
constant<T1,T2,T3,T4,R>(val:R): Function4<T1,T2,T3,T4,R> {
return Function4.of((x:T1,y:T2,z:T3,a:T4)=>val);
}
/**
* Take a four-parameter function and lift it to become a [[Function4]],
* enabling you to call [[Function4.andThen]] and other such methods on it.
*/
of<T1,T2,T3,T4,R>(fn:(x:T1,y:T2,z:T3,a:T4)=>R): Function4<T1,T2,T3,T4,R> {
const r = <Function4<T1,T2,T3,T4,R>>((x,y,z,a)=>fn(x,y,z,a));
r.andThen = <V>(fn2:(x:R)=>V) => Function4.of((x:T1,y:T2,z:T3,a:T4) => fn2(r(x,y,z,a)));
r.curried = () => Function1.of((x:T1) => Function1.of(
(y:T2) => Function1.of((z:T3) => Function1.of((a:T4)=>r(x,y,z,a)))));
r.tupled = () => Function1.of((tuple:[T1,T2,T3,T4]) => r(tuple[0],tuple[1],tuple[2],tuple[3]));
r.flipped = () => Function4.of((x:T4,y:T3,z:T2,a:T1) => r(a,z,y,x));
r.apply1 = (x:T1) => Function3.of((y:T2,z:T3,a:T4) => r(x,y,z,a));
r.apply2 = (x:T1,y:T2) => Function2.of((z:T3,a:T4) => r(x,y,z,a));
r.apply3 = (x:T1,y:T2,z:T3) => Function1.of((a:T4) => r(x,y,z,a));
return r;
}
};
/**
* The Function4 constant allows to call the [[Function4]] "static" methods.
*/
export const Function4 = new Function4Static();
/**
* This is the type of the Function5 constant, which
* offers some helper functions to deal
* with [[Function5]] including
* the ability to build [[Function5]]
* from functions using [[Function5Static.of]].
* It also offers some builtin functions like [[Function5Static.constant]].
*/
export class Function5Static {
/**
* The constant function of five parameters:
* will always return the value you give, no
* matter the parameters it's given.
*/
constant<T1,T2,T3,T4,T5,R>(val:R): Function5<T1,T2,T3,T4,T5,R> {
return Function5.of((x:T1,y:T2,z:T3,a:T4,b:T5)=>val);
}
/**
* Take a five-parameter function and lift it to become a [[Function5]],
* enabling you to call [[Function5.andThen]] and other such methods on it.
*/
of<T1,T2,T3,T4,T5,R>(fn:(x:T1,y:T2,z:T3,a:T4,b:T5)=>R): Function5<T1,T2,T3,T4,T5,R> {
const r = <Function5<T1,T2,T3,T4,T5,R>>((x,y,z,a,b)=>fn(x,y,z,a,b));
r.andThen = <V>(fn2:(x:R)=>V) => Function5.of((x:T1,y:T2,z:T3,a:T4,b:T5) => fn2(r(x,y,z,a,b)));
r.curried = () => Function1.of((x:T1) => Function1.of(
(y:T2) => Function1.of((z:T3) => Function1.of((a:T4)=>Function1.of((b:T5) => r(x,y,z,a,b))))));
r.tupled = () => Function1.of((tuple:[T1,T2,T3,T4,T5]) => r(tuple[0],tuple[1],tuple[2],tuple[3],tuple[4]));
r.flipped = () => Function5.of((x:T5,y:T4,z:T3,a:T2,b:T1) => r(b,a,z,y,x));
r.apply1 = (x:T1) => Function4.of((y:T2,z:T3,a:T4,b:T5) => r(x,y,z,a,b));
r.apply2 = (x:T1,y:T2) => Function3.of((z:T3,a:T4,b:T5) => r(x,y,z,a,b));
r.apply3 = (x:T1,y:T2,z:T3) => Function2.of((a:T4,b:T5) => r(x,y,z,a,b));
r.apply4 = (x:T1,y:T2,z:T3,a:T4) => Function1.of((b:T5) => r(x,y,z,a,b));
return r;
}
}
/**
* The Function5 constant allows to call the [[Function5]] "static" methods.
*/
export const Function5 = new Function5Static();
================================================
FILE: src/Future.ts
================================================
import { Vector } from "./Vector";
import { Option } from "./Option";
import { Either } from "./Either";
import { HashMap } from "./HashMap";
/**
* A Future is the equivalent, and ultimately wraps, a javascript Promise.
* While Futures support the [[Future.then]] call (so that among others
* you can use `await` on them), you should call [[Future.map]] and
* [[Future.flatMap]].
*
* Futures represent an asynchronous computation. A Future will only ever
* be computed once at most. Once it's computed, calling [[Future.map]] or
* `await` will return instantly.
*/
export class Future<T> {
// careful cause i can't have my type be F<F<T>>
// while the code does F<T> as JS's then does!!!
// for that reason I wrap the value in an array
// to make sure JS will never turn a Promise<Promise<T>>
// in a Promise<T>
private constructor(private promise: Promise<T[]>) { }
/**
* Build a Future in the same way as the 'new Promise'
* constructor.
* You get one callback to signal success (resolve),
* failure (reject), or you can throw to signal failure.
*
* Future.ofPromiseCtor<string>((resolve,reject) => setTimeout(resolve, 10, "hello!"))
*/
static ofPromiseCtor<T>(executor: (resolve:(x:T)=>void, reject: (x:any)=>void)=>void): Future<T> {
return new Future(new Promise(executor).then(v=>[v]));
}
/**
* Build a Future from an existing javascript Promise.
*/
static of<T>(promise: Promise<T>): Future<T> {
return new Future(promise.then(x => [x]));
}
/**
* Build a Future from a node-style callback API, for instance:
*
* Future.ofCallback<string>(cb => fs.readFile('/etc/passwd', 'utf-8', cb))
*/
static ofCallback<T>(fn: (cb:(err:any, val:T)=>void)=>void): Future<T> {
return Future.ofPromiseCtor((resolve,reject)=>fn((err, data)=>{
if (err) {
reject(err);
} else {
resolve(data);
}
}));
}
/**
* Build a successful Future with the value you provide.
*/
static ok<T>(val:T): Future<T> {
return new Future(Promise.resolve([val]));
}
/**
* Build a failed Future with the error data you provide.
*/
static failed<T>(reason: any): Future<T> {
return new Future(Promise.reject(reason));
}
/**
* Creates a Future from a function returning a Promise,
* which can be inline in the call, for instance:
*
* const f1 = Future.ok(1);
* const f2 = Future.ok(2);
* return Future.do(async () => {
* const v1 = await f1;
* const v2 = await f2;
* return v1 + v2;
* });
*/
static do<T>(fn: ()=>Promise<T>): Future<T> {
return Future.of(fn())
}
/**
* The `then` call is not meant to be a part of the `Future` API,
* we need then so that `await` works directly.
*
* Please rather use [[Future.map]] or [[Future.flatMap]].
*/
then<TResult1 = T, TResult2 = never>(
onfulfilled: ((value: T) => TResult1 | PromiseLike<TResult1>),
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2> {
return this.promise.then(([x]) => onfulfilled(x), rejected => onrejected?onrejected(rejected):Promise.reject<TResult2>(rejected));
}
/**
* Get a `Promise` from this `Future`.
*/
toPromise(): Promise<T> {
return this.promise.then(([x]) => x);
}
/**
* Returns a `Future` that'll complete when the first `Future` of
* the iterable you give will complete, with the value of that first
* future. Be careful, completing doesn't necessarily mean completing
* successfully!
*
* Also see [[Future.firstSuccessfulOf]]
*/
static firstCompletedOf<T>(elts: Iterable<Future<T>>): Future<T> {
return Future.of(
Promise.race(Vector.ofIterable(elts).map(f => f.toPromise())));
}
/**
* Returns a `Future` that'll complete when the first `Future` of
* the iterable you give will complete successfully, with the value of that first
* future.
*
* Also see [[Future.firstCompletedOf]]
*/
static firstSuccessfulOf<T>(elts: Iterable<Future<T>>): Future<T> {
// https://stackoverflow.com/a/37235274/516188
return Future.of(
Promise.all(Vector.ofIterable(elts).map(p => {
// If a request fails, count that as a resolution so it will keep
// waiting for other possible successes. If a request succeeds,
// treat it as a rejection so Promise.all immediately bails out.
return p.then(
val => Promise.reject(val),
err => Promise.resolve(err)
);
})).then(
// If '.all' resolved, we've just got an array of errors.
errors => Promise.reject(errors),
// If '.all' rejected, we've got the result we wanted.
val => Promise.resolve(val)
)
);
}
/**
* Turns a list of futures in a future containing a list of items.
* Useful in many contexts.
*
* But if a single future is failed, you get back a failed Future.
*
* Also see [[Future.traverse]]
*/
static sequence<T>(elts: Iterable<Future<T>>): Future<Vector<T>> {
return Future.traverse(elts, x=>x);
}
/**
* Takes a list, a function that can transform list elements
* to futures, then return a Future containing a list of
* the transformed elements.
*
* But if a single element results in failure, the result also
* resolves to a failure.
*
* There is an optional third parameter to specify options.
* You can specify `{maxConcurrent: number}` to request that
* the futures are not all triggered at the same time, but
* rather only 'number' at a time.
*
* Also see [[Future.sequence]]
*/
static traverse<T,U>(elts: Iterable<T>, fn: (x:T)=>Future<U>,
opts?: {maxConcurrent:number}): Future<Vector<U>> {
if (!opts) {
return Future.of(
Promise.all(Vector.ofIterable(elts).map(x => fn(x).toPromise()))
.then(Vector.ofIterable));
}
// maxConcurrent algorithm inspired by https://stackoverflow.com/a/38778887/516188
let index = 0;
let active: Future<U>[] = [];
const results: {[idx:number]:U} = {};
const it = elts[Symbol.iterator]();
let failed: Future<U>|undefined;
const addAsNeeded = (_?:U): Future<Vector<U>> => {
if (failed) {
return <any>failed;
}
let cur;
while (active.length < opts.maxConcurrent &&
!(cur = it.next()).done) {
const p = fn(cur.value);
active.push(p);
const curIdx = index++;
p.onComplete(eitherRes => {
active.splice(active.indexOf(p), 1)
if (eitherRes.isLeft()) {
failed = p;
} else {
results[curIdx] = eitherRes.get();
}
});
}
if (!failed && active.length === 0 && cur && cur.done) {
return Future.ok(
HashMap.ofObjectDictionary<U>(results)
.toVector()
.sortOn(kv => parseInt(kv[0]))
.map(kv => kv[1]));
}
return Future.firstCompletedOf(active).flatMap(addAsNeeded);
};
return addAsNeeded();
}
/**
* From the list of Futures you give, will attempt to find a successful
* Future which value matches the predicate you give.
* We return a Future of an [[Option]], which will [[None]] in case
* no matching Future is found.
*/
static find<T>(elts: Iterable<Future<T>>, p: (x: T) => boolean): Future<Option<T>> {
const origElts = Vector.ofIterable(elts)
if (origElts.isEmpty()) {
return Future.ok(Option.none<T>());
}
type FutOptPair = [Future<T>,Option<T>];
// map the failures to successes with option.none
// backup the original future object matching the new future
const velts = origElts
.map(
f => f
.map<FutOptPair>(item => [f, Option.of(item)])
.recoverWith(_=>Future.ok<FutOptPair>([f, Option.none<T>()])));
// go for the first completed of the iterable
// remember after our map they're all successful now
const success = Future.firstCompletedOf(velts);
return success
.flatMap(([originalFuture, option]) => {
if (option.isSome() && p(option.get())) {
// this successful future matches our predicate, that's it.
return success.map(x => x[1]);
} else {
// this future failed or doesn't match our predicate.
// remove the future from the input list (we can do that
// because we "backed up" the original future in the future
// result), and try again only with the remaining candidates
return Future.find(origElts.removeFirst(future => future === originalFuture), p);
}
});
}
/**
* Applicative lifting for Future. 'p' stands for 'properties'.
*
* Takes a function which operates on a simple JS object, and turns it
* in a function that operates on the same JS object type except which each field
* wrapped in a Future ('lifts' the function).
* It's an alternative to [[Future.liftA2]] when the number of parameters
* is not two.
*
* @param A the object property type specifying the parameters for your function
* @param B the type returned by your function, returned wrapped in a future by liftAp.
*/
static liftAp<A,B>(fn:(x:A)=>B): (x: {[K in keyof A]: Future<A[K]>;}) => Future<B> {
return x => {
const fieldNames: Array<keyof A> = <any>Object.keys(x);
const promisesAr = fieldNames.map(n => x[n]);
let i=0;
return Future.of(
Promise.all(promisesAr)
.then(resultAr => resultAr.reduce<{[K in keyof A]: A[K]}>((sofar,cur) => {
sofar[fieldNames[i++]] = cur;
return sofar;
}, <any>{}))).map(fn);
};
}
/**
* Applicative lifting for Future.
* Takes a function which operates on basic values, and turns it
* in a function that operates on futures of these values ('lifts'
* the function). The 2 is because it works on functions taking two
* parameters.
*
* @param R1 the first future type
* @param R2 the second future type
* @param V the new future type as returned by the combining function.
*/
static liftA2<R1,R2,V>(fn:(v1:R1,v2:R2)=>V) : (p1:Future<R1>, p2:Future<R2>) => Future<V> {
return (p1,p2) => p1.flatMap(a1 => p2.map(a2 => fn(a1,a2)));
}
/**
* Take a function returning a Promise
* and lift it to return a [[Future]] instead.
*/
static lift<T extends any[],U>(fn: (...args: T)=>Promise<U>): (...args:T)=>Future<U> {
return (...args:T) => Future.of(fn(...args));
}
/**
* Transform the value contained in a successful Future. Has no effect
* if the Future was failed. Will turn a successful Future in a failed
* one if you throw an exception in the map callback (but please don't
* do it.. Rather use [[Future.filter]] or another mechanism).
*/
map<U>(fn: (x:T)=>U): Future<U> {
return new Future<U>(this.promise.then(([x]) => [fn(x)]));
}
/**
* Transform the value contained in a successful Future. You return a
* Future, but it is then "flattened" so we still return a Future<T>
* (and not a Future<Future<T>>).
* Has no effect if the Future was failed. Will turn a successful Future in a failed
* one if you throw an exception in the map callback (but please don't
* do it.. Rather use [[Future.filter]] or another mechanism).
* This is the monadic bind.
*/
flatMap<U>(fn: (x:T)=>Future<U>): Future<U> {
return new Future<U>(this.promise.then(([x]) => fn(x).promise));
}
/**
* Transform the value contained in a failed Future. Has no effect
* if the Future was successful.
*/
mapFailure(fn: (x:any)=>any): Future<T> {
return new Future<T>(this.promise.catch(x => {throw fn(x)}));
}
/**
* Execute the side-effecting function you give if the Future is a failure.
*
* The Future is unchanged by this call.
*/
onFailure(fn: (x:any)=>void): Future<T> {
this.promise.catch(x => fn(x));
return this;
}
/**
* Execute the side-effecting function you give if the Future is a success.
*
* The Future is unchanged by this call.
*/
onSuccess(fn: (x:T)=>void): Future<T> {
// we create a new promise here, need to catch errors on it,
// to avoid node UnhandledPromiseRejectionWarning warnings
this.promise.then(x => {fn(x[0]); return x;}).catch(_ => {});
return this;
}
/**
* Execute the side-effecting function you give when the Future is
* completed. You get an [[Either]], a `Right` if the Future is a
* success, a `Left` if it's a failure.
*
* The Future is unchanged by this call.
*/
onComplete(fn: (x:Either<any,T>)=>void): Future<T> {
this.promise.then(
x => {fn(Either.right(x[0])); return x;},
x => fn(Either.left(x)));
return this;
}
/**
* Has no effect on a failed Future. If the Future was successful,
* will check whether its value matches the predicate you give as
* first parameter. If the value matches the predicate, an equivalent
* Future to the input one is returned.
*
* If the value doesn't match predicate however, the second parameter
* function is used to compute the contents of a failed Future that'll
* be returned.
*/
filter(p: (x:T)=>boolean, ifFail: (x:T)=>any): Future<T> {
return this.flatMap(
x => p(x) ? Future.ok(x) : Future.failed(ifFail(x)));
}
/**
* Has no effect if this Future is successful. If it's failed however,
* the function you give will be called, receiving as parameter
* the error contents, and a Future equivalent to the one your
* function returns will be returned.
*/
recoverWith(f: (err:any)=>Future<T>): Future<T> {
return new Future<T>(this.promise.catch(err => f(err).promise));
}
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(fn: (x:Future<T>)=>U): U {
return fn(this);
}
}
================================================
FILE: src/HashMap.ts
================================================
import { IMap } from "./IMap";
import { hasEquals, HasEquals, WithEquality,
getHashCode, areEqual, fieldsHashCode } from "./Comparison";
import { toStringHelper } from "./SeqHelpers";
import { contractTrueEquality } from "./Contract"
import { Option, none, None } from "./Option";
import { HashSet } from "./HashSet";
import { Vector } from "./Vector";
import { LinkedList } from "./LinkedList";
import * as SeqHelpers from "./SeqHelpers";
import { inspect } from "./Value";
const hamt: any = require("hamt_plus");
// HashMap could extend Collection, conceptually. But I'm
// not super happy of having the callbacks get a pair, for instance
// 'HashMap.filter' takes two parameters in the current HashMap;
// if HashMap did implement Collection, it would have to take a k,v
// pair. There's also another trick with 'contains'. The Collection signature
// says T&WithEquality, but here we get [K&WithEquality,V&WithEquality],
// but arrays don't have equality so that doesn't type-check :-(
/**
* A dictionary, mapping keys to values.
* @param K the key type
* @param V the value type
*/
export class HashMap<K,V> implements IMap<K,V> {
/**
* @hidden
*/
protected constructor(private hamt: any) {}
/**
* The empty map.
* @param K the key type
* @param V the value type
*/
static empty<K,V>(): HashMap<K,V> {
return <EmptyHashMap<K,V>>emptyHashMap;
}
/**
* Build a HashMap from key-value pairs.
*
* HashMap.of([1,"a"],[2,"b"])
*
*/
static of<K,V>(...entries: Array<[K&WithEquality, V]>): HashMap<K,V> {
return HashMap.ofIterable<K,V>(entries);
}
/**
* Build a HashMap from an iterable containing key-value pairs.
*
* HashMap.ofIterable(Vector.of<[number,string]>([1,"a"],[2,"b"]));
*/
static ofIterable<K,V>(entries: Iterable<[K&WithEquality, V]>): HashMap<K,V> {
// remember we must set up the hamt with the custom equality
const iterator = entries[Symbol.iterator]();
let curItem = iterator.next();
if (curItem.done) {
return new EmptyHashMap<K,V>();
}
// emptyhashmap.put sets up the custom equality+hashcode
let startH = (new EmptyHashMap<K,V>()).put(curItem.value[0], curItem.value[1]).hamt;
curItem = iterator.next();
return new HashMap<K,V>(startH.mutate((h:any) => {
while (!curItem.done) {
h.set(curItem.value[0], curItem.value[1]);
curItem = iterator.next();
}
}));
}
/**
* Build a HashMap from a javascript object literal representing
* a dictionary. Note that the key type must always be string,
* as that's the way it works in javascript.
* Also note that entries with undefined values will be stripped
* from the map.
*
* HashMap.ofObjectDictionary<number>({a:1,b:2})
* => HashMap.of(["a",1],["b",2])
*/
static ofObjectDictionary<V>(object: {[index:string]: V|undefined}): HashMap<string,V> {
// no need to bother with the proper equals & hashcode
// as I know the key type supports ===
const h: any = hamt.make().beginMutation();
for (let property in object) {
// the reason we strip entries with undefined values on
// import from object dictionaries are: sanity, and also
// partial object definitions like {[TKey in MyEnum]?:number}
// where typescript sees the value type as 'number|undefined'
// (there is a test covering that)
if (object.hasOwnProperty(property) &&
(typeof object[property] !== "undefined")) {
h.set(property, object[property]);
}
}
return new HashMap<string,V>(h.endMutation());
}
/**
* Curried predicate to find out whether the HashMap is empty.
*
* Vector.of(HashMap.of([1,2]), HashMap.empty<number,number>())
* .filter(HashMap.isEmpty)
* => Vector.of(HashMap.empty<number,number>())
*/
static isEmpty<K,V>(v: HashMap<K,V>): boolean {
return v.isEmpty();
}
/**
* Curried predicate to find out whether the HashMap is empty.
*
* Vector.of(HashMap.of([1,2]), HashMap.empty<number,number>())
* .filter(HashMap.isNotEmpty)
* => Vector.of(HashMap.of([1,2]))
*/
static isNotEmpty<K,V>(v: HashMap<K,V>): boolean {
return !v.isEmpty();
}
/**
* Get the value for the key you give, if the key is present.
*/
get(k: K & WithEquality): Option<V> {
return Option.of<V>(this.hamt.get(k));
}
/**
* Implementation of the Iterator interface.
*/
[Symbol.iterator](): Iterator<[K,V]> {
return this.hamt.entries();
}
/**
* @hidden
*/
hasTrueEquality(): boolean {
// for true equality, need both key & value to have true
// equality. but i can't check when they're in an array,
// as array doesn't have true equality => extract them
// and check them separately.
return Option.of(this.hamt.entries().next().value)
.map(x => x[0]).hasTrueEquality() &&
Option.of(this.hamt.entries().next().value)
.map(x => x[1]).hasTrueEquality()
}
/**
* Add a new entry in the map. If there was entry with the same
* key, it will be overwritten.
* @param k the key
* @param v the value
*/
put(k: K & WithEquality, v: V): HashMap<K,V> {
return new HashMap<K,V>(this.hamt.set(k,v));
}
/**
* Return a new map with the key you give removed.
*/
remove(k: K&WithEquality): HashMap<K,V> {
return new HashMap<K,V>(this.hamt.remove(k));
}
/**
* Add a new entry in the map; in case there was already an
* entry with the same key, the merge function will be invoked
* with the old and the new value to produce the value to take
* into account.
*
* It is guaranteed that the merge function first parameter
* will be the entry from this map, and the second parameter
* from the map you give.
* @param k the key
* @param v the value
* @param merge a function to merge old and new values in case of conflict.
*/
putWithMerge(k: K & WithEquality, v: V, merge: (v1: V, v2: V) => V): HashMap<K,V> {
return new HashMap<K,V>(this.hamt.modify(k, (curV?: V) => {
if (curV === undefined) {
return v;
}
return merge(curV, v);
}))
}
/**
* number of items in the map
*/
length(): number {
return this.hamt.size;
}
/**
* If the collection contains a single element,
* return Some of its value, otherwise return None.
*/
single(): Option<[K,V]> {
return this.hamt.size === 1
? Option.of(this.hamt.entries().next().value)
: Option.none();
}
/**
* true if the map is empty, false otherwise.
*/
isEmpty(): boolean {
return this.hamt.size === 0;
}
/**
* Get a Set containing all the keys in the map
*/
keySet(): HashSet<K> {
return HashSet.ofIterable<K>(this.hamt.keys());
}
/**
* Get an iterable containing all the values in the map
* (can't return a set as we don't constrain map values
* to have equality in the generics type)
*/
valueIterable(): Iterable<V> {
const hamt = this.hamt;
return {
[Symbol.iterator]() { return hamt.values(); }
};
}
/**
* Create a new map combining the entries of this map, and
* the other map you give. In case an entry from this map
* and the other map have the same key, the merge function
* will be invoked to get a combined value.
*
* It is guaranteed that the merge function first parameter
* will be the entry from this map, and the second parameter
* from the map you give.
* @param other another map to merge with this one
* @param merge a merge function to combine two values
* in case two entries share the same key.
*/
mergeWith(elts: Iterable<[K & WithEquality,V]>, merge:(v1: V, v2: V) => V): HashMap<K,V> {
const iterator = elts[Symbol.iterator]();
let map: HashMap<K,V> = this;
let curItem = iterator.next();
while (!curItem.done) {
map = map.putWithMerge(curItem.value[0], curItem.value[1], merge);
curItem = iterator.next();
}
return map;
}
/**
* Return a new map where each entry was transformed
* by the mapper function you give. You return key,value
* as pairs.
*/
map<K2,V2>(fn:(k:K&WithEquality, v:V)=>[K2&WithEquality,V2]): HashMap<K2,V2> {
return this.hamt.fold(
(acc: HashMap<K2,V2>, value: V, key: K&WithEquality) => {
const [newk,newv] = fn(key, value);
return acc.put(newk,newv);
}, HashMap.empty());
}
/**
* Return a new map where keys are the same as in this one,
* but values are transformed
* by the mapper function you give. You return key,value
* as pairs.
*/
mapValues<V2>(fn:(v:V)=>V2): HashMap<K,V2> {
return this.hamt.fold(
(acc: HashMap<K,V2>, value: V, key: K&WithEquality) =>
acc.put(key,fn(value)), HashMap.empty());
}
/**
* Call a function for element in the collection.
*/
forEach(fun:(x:[K,V])=>void): HashMap<K,V> {
const iterator: Iterator<[K,V]> = this.hamt.entries();
let curItem = iterator.next();
while (!curItem.done) {
fun(curItem.value);
curItem = iterator.next();
}
return this;
}
/**
* Calls the function you give for each item in the map,
* your function returns a map, all the maps are
* merged.
*/
flatMap<K2,V2>(fn:(k:K, v:V)=>Iterable<[K2&WithEquality,V2]>): HashMap<K2,V2> {
return this.foldLeft(HashMap.empty<K2,V2>(),
(soFar,cur) => soFar.mergeWith(fn(cur[0],cur[1]), (a,b)=>b));
}
/**
* Returns true if the predicate returns true for all the
* elements in the collection.
*/
allMatch(predicate:(k:K,v:V)=>boolean): boolean {
const iterator: Iterator<[K,V]> = this.hamt.entries();
let curItem = iterator.next();
while (!curItem.done) {
if (!predicate(curItem.value[0], curItem.value[1])) {
return false;
}
curItem = iterator.next();
}
return true;
}
/**
* Returns true if there the predicate returns true for any
* element in the collection.
*/
anyMatch(predicate:(k:K,v:V)=>boolean): boolean {
const iterator: Iterator<[K,V]> = this.hamt.entries();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value[0], curItem.value[1])) {
return true;
}
curItem = iterator.next();
}
return false;
}
/**
* Returns true if the item is in the collection,
* false otherwise.
*/
contains(val: [K&WithEquality,V&WithEquality]): boolean {
return areEqual(this.hamt.get(val[0]), val[1]);
}
/**
* Returns true if there is item with that key in the collection,
* false otherwise.
*
* HashMap.of<number,string>([1,"a"],[2,"b"]).containsKey(1);
* => true
*
* HashMap.of<number,string>([1,"a"],[2,"b"]).containsKey(3);
* => false
*/
containsKey(key: K&WithEquality): boolean {
return this.hamt.has(key);
}
/**
* Call a predicate for each element in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filter(predicate:(k:K,v:V)=>boolean): HashMap<K,V> {
return new HashMap<K,V>(
hamt.make({hash:this.hamt._config.hash, keyEq:this.hamt._config.keyEq}).mutate((h:any) => {
const iterator: Iterator<[K,V]> = this.hamt.entries();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value[0], curItem.value[1])) {
h.set(curItem.value[0], curItem.value[1]);
}
curItem = iterator.next();
}
}));
}
/**
* Search for an item matching the predicate you pass,
* return Option.Some of that element if found,
* Option.None otherwise.
* We name the method findAny instead of find to emphasize
* that there is not ordering in a hashset.
*
* HashMap.of<number,string>([1,'a'],[2,'b'],[3,'c'])
* .findAny((k,v) => k>=2 && v === "c")
* => Option.of([3,'c'])
*
* HashMap.of<number,string>([1,'a'],[2,'b'],[3,'c'])
* .findAny((k,v) => k>=3 && v === "b")
* => Option.none<[number,string]>()
*/
findAny(predicate:(k:K,v:V)=>boolean): Option<[K,V]> {
const iterator: Iterator<[K,V]> = this.hamt.entries();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value[0], curItem.value[1])) {
return Option.of(curItem.value);
}
curItem = iterator.next();
}
return Option.none<[K,V]>();
}
/**
* Call a predicate for each key in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*
* HashMap.of<number,string>([1,"a"],[2,"b"]).filterKeys(k=>k%2===0)
* => HashMap.of<number,string>([2,"b"])
*/
filterKeys<U extends K>(fn:(v:K)=>v is U): HashMap<U,V>;
filterKeys(predicate:(k:K)=>boolean): HashMap<K,V>;
filterKeys(predicate:(k:K)=>boolean): HashMap<K,V> {
return this.filter((k,v)=>predicate(k));
}
/**
* Call a predicate for each value in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*
* HashMap.of<number,string>([1,"a"],[2,"ab"]).filterValues(v=>v.length>1)
* => HashMap.of<number,string>([2,"ab"])
*/
filterValues<U extends V>(fn:(v:V)=>v is U): HashMap<K,U>;
filterValues(predicate:(k:V)=>boolean): HashMap<K,V>;
filterValues(predicate:(k:V)=>boolean): HashMap<K,V> {
return this.filter((k,v)=>predicate(v));
}
/**
* Reduces the collection to a single value using the
* associative binary function you give. Since the function
* is associative, order of application doesn't matter.
*
* Example:
*
* HashMap.of<number,string>([1,"a"],[2,"b"],[3,"c"])
* .fold([0,""], ([a,b],[c,d])=>[a+c, b>d?b:d])
* => [6,"c"]
*/
fold(zero:[K,V], fn:(v1:[K,V],v2:[K,V])=>[K,V]): [K,V] {
return this.foldLeft(zero, fn);
}
/**
* Reduces the collection to a single value.
* Left-associative.
* No guarantees for the order of items in a hashset!
*
* Example:
*
* HashMap.of([1,"a"], [2,"bb"], [3,"ccc"])
* .foldLeft(0, (soFar,[item,val])=>soFar+val.length);
* => 6
*
* @param zero The initial value
* @param fn A function taking the previous value and
* the current collection item, and returning
* an updated value.
*/
foldLeft<U>(zero: U, fn:(soFar:U,cur:[K,V])=>U): U {
return this.hamt.fold(
(acc: U, v: V, k: K&WithEquality) =>
fn(acc, [k,v]), zero);
}
/**
* Reduces the collection to a single value.
* Right-associative.
* No guarantees for the order of items in a hashset!
*
* Example:
*
* HashMap.of([1,"a"], [2,"bb"], [3,"ccc"])
* .foldRight(0, ([item,value],soFar)=>soFar+value.length);
* => 6
*
* @param zero The initial value
* @param fn A function taking the current collection item and
* the previous value , and returning
* an updated value.
*/
foldRight<U>(zero: U, fn:(cur:[K,V], soFar:U)=>U): U {
return this.foldLeft(zero, (cur, soFar) => fn(soFar, cur));
}
/**
* Reduces the collection to a single value by repeatedly
* calling the combine function.
* No starting value. The order in which the elements are
* passed to the combining function is undetermined.
*/
reduce(combine: (v1:[K,V],v2:[K,V])=>[K,V]): Option<[K,V]> {
// not really glorious with any...
return <Option<[K,V]>>SeqHelpers.reduce<any>(<any>this, combine);
}
/**
* Convert to array.
*/
toArray(): Array<[K,V]> {
return this.hamt.fold(
(acc: [[K,V]], value: V, key: K&WithEquality) =>
{acc.push([key,value]); return acc; }, []);
}
/**
* Convert this map to a vector of key,value pairs.
* Note that Map is already an iterable of key,value pairs!
*/
toVector(): Vector<[K,V]> {
return this.hamt.fold(
(acc: Vector<[K,V]>, value: V, key: K&WithEquality) =>
acc.append([key,value]), Vector.empty());
}
/**
* Convert this map to a list of key,value pairs.
* Note that Map is already an iterable of key,value pairs!
*/
toLinkedList(): LinkedList<[K,V]> {
return LinkedList.ofIterable(this);
}
/**
* Convert to a javascript object dictionary
* You must provide a function to convert the
* key to a string.
*
* HashMap.of<string,number>(["a",1],["b",2])
* .toObjectDictionary(x=>x);
* => {a:1,b:2}
*/
toObjectDictionary(keyConvert:(k:K)=>string): {[index:string]:V} {
return this.foldLeft<{[index:string]:V}>({}, (soFar,cur)=> {
soFar[keyConvert(cur[0])] = cur[1];
return soFar;
});
}
/**
* Convert to an ES6 Map.
* You must provide a function to convert the
* key to a string, number or boolean, because
* with other types equality is not correctly
* managed by JS.
* https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set
* https://esdiscuss.org/topic/maps-with-object-keys
*
* HashMap.of<string,number>(["a",1],["b",2])
* .toJsMap(x=>x);
* => new Map([["a",1], ["b",2]])
*/
toJsMap(keyConvert:(k:K)=>string): Map<string,V>;
toJsMap(keyConvert:(k:K)=>number): Map<number,V>;
toJsMap(keyConvert:(k:K)=>boolean): Map<boolean,V>;
toJsMap<K2 extends number|string|boolean>(keyConvert:(k:K)=>K2): Map<K2,V> {
return this.foldLeft(
new Map<K2,V>(),
(soFar,cur)=> soFar.set(keyConvert(cur[0]), cur[1]));
}
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:HashMap<K,V>)=>U): U {
return converter(this);
}
/**
* Two objects are equal if they represent the same value,
* regardless of whether they are the same object physically
* in memory.
*/
equals(other: IMap<K&WithEquality,V&WithEquality>): boolean {
if (<any>other === this) {
return true;
}
if (!other || !other.valueIterable) {
return false;
}
contractTrueEquality("HashMap.equals", this, other);
const sz = this.hamt.size;
if (other.length() === 0 && sz === 0) {
// we could get that i'm not the empty map
// but my size is zero, after some filtering and such.
return true;
}
if (sz !== other.length()) {
return false;
}
const keys: Array<K & WithEquality> = Array.from<K & WithEquality>(this.hamt.keys());
for (let k of keys) {
const myVal: V|null|undefined = this.hamt.get(k);
const hisVal: V|null|undefined = other.get(k).getOrUndefined();
if (myVal === undefined || hisVal === undefined) {
return false;
}
if (!areEqual(myVal, hisVal)) {
return false;
}
}
return true;
}
/**
* Get a number for that object. Two different values
* may get the same number, but one value must always get
* the same number. The formula can impact performance.
*/
hashCode(): number {
// references:
// https://github.com/clojure/clojure/blob/5ffe3833508495ca7c635d47ad7a1c8b820eab76/src/jvm/clojure/lang/APersistentMap.java#L98
// https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/19fb8f93c59dfd791f62d41f332db9e306bc1422/src/java.base/share/classes/java/util/HashMap.java#L296
// Both the Clojure and AdoptOpenJDK references calculate the bitwise XOR
// of the key/value pairs. The significance of the bitwise XOR is that,
// unlike just adding the hashcode of the key and value pair, they become
// intertwined, which prevents collisions when identical values are swapped
// between keys.
// This algorithm only intertwines the keys and the values. The calculations
// for the pairs are summed up, to make sure that we get the same result
// if the pairs are in a different order.
// The Clojure implementation also caches the result of the calculation, but
// we've decided not to do that yet, see the discussion on the PR for context:
// https://github.com/emmanueltouzery/prelude-ts/pull/67
return this.hamt.fold(
(acc: number, value: V, key: K & WithEquality) =>
acc + (getHashCode(key) ^ getHashCode(value)), 0);
}
/*
* Get a human-friendly string representation of that value.
*/
toString(): string {
return "HashMap(" +
this.hamt.fold(
(acc: string[], value: V, key: K) =>
{acc.push(
toStringHelper(key, {quoteStrings:false}) +
": " + toStringHelper(value)); return acc;}, [])
.join(", ") + ")";
}
[inspect](): string {
return this.toString();
}
}
// we need to override the empty hashmap
// because i don't know how to get the hash & keyset
// functions for the keys without a key value to get
// the functions from
class EmptyHashMap<K,V> extends HashMap<K,V> {
constructor() {
super({}); // we must override all the functions
}
get(k: K & WithEquality): Option<V> {
return <None<V>>none;
}
[Symbol.iterator](): Iterator<[K,V]> {
return { next: () => ({ done: true, value: <any>undefined }) };
}
put(k: K & WithEquality, v: V): HashMap<K,V> {
contractTrueEquality("Error building a HashMap", k);
if (hasEquals(k)) {
return new HashMap<K,V>(hamt.make({
hash: (v: K & HasEquals) => v.hashCode(),
keyEq: (a: K & HasEquals, b: K & HasEquals) => a.equals(b)
}).set(k,v));
}
return new HashMap<K,V>(hamt.make().set(k,v));
}
remove(k: K&WithEquality): HashMap<K,V> {
return this;
}
hasTrueEquality(): boolean {
return true;
}
putWithMerge(k: K & WithEquality, v: V, merge: (v1: V, v2: V) => V): HashMap<K,V> {
return this.put(k,v);
}
length(): number {
return 0;
}
/**
* If the collection contains a single element,
* return Some of its value, otherwise return None.
*/
single(): Option<[K,V]> {
return Option.none<[K,V]>();
}
isEmpty(): boolean {
return true;
}
keySet(): HashSet<K> {
return HashSet.empty<K>();
}
valueIterable(): Iterable<V> {
return {
[Symbol.iterator](): Iterator<V> {
return {
next(): IteratorResult<V> {
return {
done: true,
value: <any>undefined
};
}
};
}
};
}
mergeWith(other: Iterable<[K & WithEquality,V]>, merge:(v1: V, v2: V) => V): HashMap<K,V> {
return HashMap.ofIterable(other);
}
map<K2,V2>(fn:(k:K&WithEquality, v:V)=>[K2&WithEquality,V2]): HashMap<K2,V2> {
return HashMap.empty<K2,V2>();
}
mapValues<V2>(fn:(v:V)=>V2): HashMap<K,V2> {
return HashMap.empty<K,V2>();
}
forEach(fun:(x:[K,V])=>void): HashMap<K,V> {
return this;
}
allMatch(predicate:(k:K,v:V)=>boolean): boolean {
return true;
}
anyMatch(predicate:(k:K,v:V)=>boolean): boolean {
return false;
}
contains(val: [K&WithEquality,V&WithEquality]): boolean {
return false;
}
containsKey(key: K&WithEquality) : boolean {
return false;
}
filter(predicate:(k:K,v:V)=>boolean): HashMap<K,V> {
return this;
}
findAny(predicate:(k:K,v:V)=>boolean): Option<[K,V]> {
return Option.none<[K,V]>();
}
foldLeft<U>(zero: U, fn:(soFar:U,cur:[K,V])=>U): U {
return zero;
}
toArray(): Array<[K,V]> {
return [];
}
toVector(): Vector<[K,V]> {
return Vector.empty<[K,V]>();
}
toLinkedList(): LinkedList<[K,V]> {
return LinkedList.empty<[K,V]>();
}
equals(other: IMap<K&WithEquality,V&WithEquality>): boolean {
if (!other || !other.valueIterable) {
return false;
}
return <any>other === emptyHashMap || other.length() === 0;
}
hashCode(): number {
return 0;
}
toString(): string {
return "HashMap()";
}
}
const emptyHashMap = new EmptyHashMap<any,any>();
================================================
FILE: src/HashSet.ts
================================================
import { ISet,
SortOnSpec, SortBySpec, isSortOnSpec } from "./ISet";
import { Vector } from "./Vector";
import { HashMap } from "./HashMap";
import { LinkedList } from "./LinkedList";
import { Option } from "./Option";
import { WithEquality, hasEquals, HasEquals,
getHashCode, areEqual, Ordering, ToOrderable } from "./Comparison";
import * as SeqHelpers from "./SeqHelpers";
import { contractTrueEquality } from "./Contract";
import { inspect } from "./Value";
const hamt: any = require("hamt_plus");
/**
* An unordered collection of values, where no two values
* may be equal. A value can only be present once.
* @param T the item type
*/
export class HashSet<T> implements ISet<T> {
/**
* @hidden
*/
protected constructor(private hamt: any) {}
/**
* The empty hashset.
* @param T the item type
*/
static empty<T>(): HashSet<T> {
return <EmptyHashSet<T>>emptyHashSet;
}
/**
* Build a hashset from any iterable, which means also
* an array for instance.
* @param T the item type
*/
static ofIterable<T>(elts: Iterable<T & WithEquality>): HashSet<T> {
return new EmptyHashSet<T>().addAll(elts);
}
/**
* Build a hashset from a series of items (any number, as parameters)
* @param T the item type
*/
static of<T>(...arr: Array<T & WithEquality>): HashSet<T> {
return HashSet.ofIterable(arr);
}
/**
* Curried predicate to find out whether the HashSet is empty.
*
* Vector.of(HashSet.of(1), HashSet.empty<number>())
* .filter(HashSet.isEmpty)
* => Vector.of(HashSet.empty<number>())
*/
static isEmpty<T>(v: HashSet<T>): boolean {
return v.isEmpty();
}
/**
* Curried predicate to find out whether the HashSet is empty.
*
* Vector.of(HashSet.of(1), HashSet.empty<number>())
* .filter(HashSet.isNotEmpty)
* => Vector.of(HashSet.of(1))
*/
static isNotEmpty<T>(v: HashSet<T>): boolean {
return !v.isEmpty();
}
/**
* Implementation of the Iterator interface.
*/
[Symbol.iterator](): Iterator<T> {
return this.hamt.keys();
}
/**
* Add an element to this set.
*/
add(elt: T & WithEquality): HashSet<T> {
return new HashSet<T>(this.hamt.set(elt,elt));
}
private addAllArray(elts: Array<T&WithEquality>): HashSet<T> {
return new HashSet<T>(this.hamt.mutate((h:any) => {
if (elts.length > 0) {
contractTrueEquality("Error building a HashSet", elts[0]);
}
for (const val of elts) {
h.set(val, val);
}
}));
}
/**
* Add multiple elements to this set.
*/
addAll(elts: Iterable<T & WithEquality>): HashSet<T> {
if (Array.isArray(elts)) {
return this.addAllArray(elts);
}
return new HashSet<T>(this.hamt.mutate((h:any) => {
let checkedEq = false;
const iterator = elts[Symbol.iterator]();
let curItem = iterator.next();
if (!curItem.done && curItem.value && !checkedEq) {
contractTrueEquality("Error building a HashSet", curItem.value);
checkedEq = true;
}
while (!curItem.done) {
h.set(curItem.value, curItem.value);
curItem = iterator.next();
}
}));
}
/**
* Returns true if the element you give is present in
* the set, false otherwise.
*/
contains(elt: T & WithEquality): boolean {
return this.hamt.has(elt);
}
/**
* Return a new collection where each element was transformed
* by the mapper function you give.
* The resulting set may be smaller than the source.
*/
map<U>(mapper:(v:T)=>U&WithEquality): HashSet<U> {
return this.hamt.fold(
(acc: HashSet<U>, value: T&WithEquality, key: T&WithEquality) => {
return acc.add(mapper(value));
}, HashSet.empty());
}
/**
* Apply the mapper function on every element of this collection.
* The mapper function returns an Option; if the Option is a Some,
* the value it contains is added to the result Collection, if it's
* a None, the value is discarded.
*
* HashSet.of(1,2,6).mapOption(x => x%2===0 ?
* Option.of(x+1) : Option.none<number>())
* => HashSet.of(3, 7)
*/
mapOption<U>(mapper:(v:T)=>Option<U&WithEquality>): HashSet<U> {
return this.hamt.fold(
(acc: HashSet<U>, value: T&WithEquality, key: T&WithEquality) => {
const val = mapper(value);
return val.isSome() ? acc.add(val.get()) : acc
}, HashSet.empty());
}
/**
* Call a function for element in the collection.
*/
forEach(fun:(x:T)=>void): HashSet<T> {
const iterator: Iterator<T> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
fun(curItem.value);
curItem = iterator.next();
}
return this;
}
/**
* Calls the function you give for each item in the set,
* your function returns a set, all the sets are
* merged.
*/
flatMap<U>(mapper:(v:T)=>HashSet<U&WithEquality>): HashSet<U> {
return this.foldLeft(HashSet.empty<U>(),
(soFar,cur) => soFar.addAll(mapper(cur)));
}
/**
* Call a predicate for each element in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filter<U extends T>(fn:(v:T)=>v is U): HashSet<U>;
filter(predicate:(v:T)=>boolean): HashSet<T>;
filter(predicate:(v:T)=>boolean): HashSet<T> {
return new HashSet<T>(
hamt.make({hash:this.hamt._config.hash, keyEq:this.hamt._config.keyEq}).mutate((h:any) => {
const iterator: Iterator<T> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value)) {
h.set(curItem.value, curItem.value);
}
curItem = iterator.next();
}
}));
}
/**
* Search for an item matching the predicate you pass,
* return Option.Some of that element if found,
* Option.None otherwise.
* We name the method findAny instead of find to emphasize
* that there is not ordering in a hashset.
*
* HashSet.of(1,2,3).findAny(x => x>=3)
* => Option.of(3)
*
* HashSet.of(1,2,3).findAny(x => x>=4)
* => Option.none<number>()
*/
findAny(predicate:(v:T)=>boolean): Option<T> {
const iterator: Iterator<T> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value)) {
return Option.of(curItem.value);
}
curItem = iterator.next();
}
return Option.none<T>();
}
/**
* Reduces the collection to a single value using the
* associative binary function you give. Since the function
* is associative, order of application doesn't matter.
*
* Example:
*
* HashSet.of(1,2,3).fold(0, (a,b) => a + b);
* => 6
*/
fold(zero:T, fn:(v1:T,v2:T)=>T): T {
return this.foldLeft(zero, fn);
}
/**
* Reduces the collection to a single value.
* Left-associative.
* No guarantees for the order of items in a hashset!
*
* Example:
*
* HashSet.of("a", "bb", "ccc").foldLeft(0, (soFar,item) => soFar+item.length);
* => 6
*
* @param zero The initial value
* @param fn A function taking the previous value and
* the current collection item, and returning
* an updated value.
*/
foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
return this.hamt.fold(
(acc: U, v: T&WithEquality, k: T&WithEquality) =>
fn(acc, v), zero);
}
/**
* Reduces the collection to a single value.
* Right-associative.
* No guarantees for the order of items in a hashset!
*
* Example:
*
* HashSet.of("a", "bb", "ccc").foldRight(0, (item,soFar) => soFar+item.length);
* => 6
*
* @param zero The initial value
* @param fn A function taking the current collection item and
* the previous value , and returning
* an updated value.
*/
foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
return this.foldLeft(zero, (cur, soFar) => fn(soFar, cur));
}
/**
* Converts this set to an array. Since a Set is not ordered
* and since this method returns a JS array, it can be awkward
* to get an array sorted in the way you'd like. So you can pass
* an optional sorting function too.
*
* HashSet.of(1,2,3).toArray().sort()
* => [1,2,3]
*
* HashSet.of(1,2,3).toArray({sortOn:x=>x})
* => [1,2,3]
*
* HashSet.of(1,2,3).toArray({sortBy:(x,y)=>x-y})
* => [1,2,3]
*
* You can also pass an array in sortOn, listing lambdas to
* several fields to sort by those fields, and also {desc:lambda}
* to sort by some fields descending.
*/
toArray(sort?: SortOnSpec<T> | SortBySpec<T>): Array<T & WithEquality> {
if (!sort) {
return Array.from<T&WithEquality>(this.hamt.keys());
}
if (isSortOnSpec(sort)) {
const sortOn = sort.sortOn instanceof Array ? sort.sortOn : [sort.sortOn];
return Vector.ofIterable<T&WithEquality>(this.hamt.keys())
.sortOn(...sortOn)
.toArray();
}
return Array.from<T&WithEquality>(this.hamt.keys()).sort(sort.sortBy);
}
/**
* Converts this set to an vector
*/
toVector(): Vector<T & WithEquality> {
return Vector.ofIterable<T&WithEquality>(this.hamt.keys());
}
/**
* Converts this set to an list
*/
toLinkedList(): LinkedList<T & WithEquality> {
return LinkedList.ofIterable<T&WithEquality>(this.hamt.keys());
}
/**
* Returns the number of elements in the set.
*/
length(): number {
return this.hamt.size;
}
/**
* If the collection contains a single element,
* return Some of its value, otherwise return None.
*/
single(): Option<T> {
return this.hamt.size === 1
? Option.of(this.hamt.keys().next().value)
: Option.none();
}
/**
* true if the set is empty, false otherwise.
*/
isEmpty(): boolean {
return this.hamt.size === 0;
}
/**
* Returns a new Set containing the difference
* between this set and the other Set passed as parameter.
* also see [[HashSet.intersect]]
*/
diff(elts: ISet<T&WithEquality>): HashSet<T> {
return new HashSet<T>(this.hamt.fold(
(acc: any, v: T&WithEquality, k: T&WithEquality) =>
elts.contains(k) ? acc : acc.set(k,k), hamt.empty));
}
/**
* Returns a new Set containing the intersection
* of this set and the other Set passed as parameter
* (the elements which are common to both sets)
* also see [[HashSet.diff]]
*/
intersect(other: ISet<T&WithEquality>): HashSet<T> {
return new HashSet<T>(this.hamt.fold(
(acc: any, v: T&WithEquality, k: T&WithEquality) =>
other.contains(k) ? acc.set(k,k) : acc, hamt.empty));
}
isSubsetOf(other: ISet<T&WithEquality>): boolean {
return this.allMatch((x:T) => other.contains(<T&WithEquality>x));
}
/**
* Returns a new set with the element you give removed
* if it was present in the set.
*/
remove(elt: T&WithEquality): HashSet<T> {
return new HashSet<T>(this.hamt.remove(elt));
}
/**
* Returns a new set with all the elements of the current
* Set, minus the elements of the iterable you give as a parameter.
* If you call this function with a HashSet as parameter,
* rather call 'diff', as it'll be faster.
*/
removeAll(elts: Iterable<T&WithEquality>): HashSet<T> {
return this.diff(HashSet.ofIterable<T&WithEquality>(elts));
}
/**
* Returns true if the predicate returns true for all the
* elements in the collection.
*/
allMatch<U extends T>(predicate:(v:T)=>v is U): this is HashSet<U>;
allMatch(predicate:(v:T)=>boolean): boolean;
allMatch(predicate:(v:T)=>boolean): boolean {
const iterator: Iterator<T> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
if (!predicate(curItem.value)) {
return false;
}
curItem = iterator.next();
}
return true;
}
/**
* Returns true if there the predicate returns true for any
* element in the collection.
*/
anyMatch(predicate:(v:T)=>boolean): boolean {
const iterator: Iterator<T> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value)) {
return true;
}
curItem = iterator.next();
}
return false;
}
/**
* Group elements in the collection using a classifier function.
* Elements are then organized in a map. The key is the value of
* the classifier, and in value we get the list of elements
* matching that value.
*
* also see [[HashSet.arrangeBy]]
*/
groupBy<C>(classifier: (v:T)=>C&WithEquality): HashMap<C,HashSet<T>> {
// make a singleton set with the same equality as this
const singletonHamtSet = (v:T) => hamt.make({
hash:this.hamt._config.hash, keyEq:this.hamt._config.keyEq
}).set(v,v);
// merge two mutable hamt sets, but I know the second has only 1 elt
const mergeSets = (v1:any,v2:any)=> {
const k = v2.keys().next().value;
v1.set(k,k);
return v1;
};
return this.hamt.fold(
// fold operation: combine a new value from the set with the accumulator
(acc: HashMap<C,any>, v:T&WithEquality, k:T&WithEquality) =>
acc.putWithMerge(
classifier(v), singletonHamtSet(v).beginMutation(),
mergeSets),
// fold accumulator: the empty hashmap
HashMap.empty())
.mapValues((h:any) => new HashSet<T>(h.endMutation()));
}
/**
* Matches each element with a unique key that you extract from it.
* If the same key is present twice, the function will return None.
*
* also see [[HashSet.groupBy]]
*/
arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
return SeqHelpers.arrangeBy<T,K>(this, getKey);
}
/**
* Returns a pair of two sets; the first one
* will only contain the items from this sets for
* which the predicate you give returns true, the second
* will only contain the items from this collection where
* the predicate returns false.
*
* HashSet.of(1,2,3,4).partition(x => x%2===0)
* => [HashSet.of(2,4), HashSet.of(1,3)]
*/
partition<U extends T>(predicate:(v:T)=>v is U): [HashSet<U>,HashSet<Exclude<T,U>>];
partition(predicate:(x:T)=>boolean): [HashSet<T>,HashSet<T>];
partition(predicate:(v:T)=>boolean): [HashSet<T>,HashSet<T>] {
let r1 = hamt.make({
hash:this.hamt._config.hash, keyEq:this.hamt._config.keyEq
}).beginMutation();
let r2 = hamt.make({
hash:this.hamt._config.hash, keyEq:this.hamt._config.keyEq
}).beginMutation();
const iterator: Iterator<T&WithEquality> = this.hamt.values();
let curItem = iterator.next();
while (!curItem.done) {
if (predicate(curItem.value)) {
r1.set(curItem.value, curItem.value);
} else {
r2.set(curItem.value, curItem.value);
}
curItem = iterator.next();
}
return [new HashSet<T>(r1), new HashSet<T>(r2)];
}
/**
* Reduces the collection to a single value by repeatedly
* calling the combine function.
* No starting value. The order in which the elements are
* passed to the combining function is undetermined.
*/
reduce(combine: (v1:T,v2:T)=>T): Option<T> {
return SeqHelpers.reduce(this, combine);
}
/**
* Compare values in the collection and return the smallest element.
* Returns Option.none if the collection is empty.
*
* also see [[HashSet.minOn]]
*/
minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
return SeqHelpers.minBy(this, compare);
}
/**
* Call the function you give for each value in the collection
* and return the element for which the result was the smallest.
* Returns Option.none if the collection is empty.
*
* also see [[HashSet.minBy]]
*/
minOn(getOrderable: ToOrderable<T>): Option<T> {
return SeqHelpers.minOn(this, getOrderable);
}
/**
* Compare values in the collection and return the largest element.
* Returns Option.none if the collection is empty.
*
* also see [[HashSet.maxOn]]
*/
maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
return SeqHelpers.maxBy(this, compare);
}
/**
* Call the function you give for each value in the collection
* and return the element for which the result was the largest.
* Returns Option.none if the collection is empty.
*
* also see [[HashSet.maxBy]]
*/
maxOn(getOrderable: ToOrderable<T>): Option<T> {
return SeqHelpers.maxOn(this, getOrderable);
}
/**
* Call the function you give for each element in the collection
* and sum all the numbers, return that sum.
* Will return 0 if the collection is empty.
*
* HashSet.of(1,2,3).sumOn(x=>x)
* => 6
*/
sumOn(getNumber: (v:T)=>number): number {
return SeqHelpers.sumOn(this, getNumber);
}
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:HashSet<T>)=>U): U {
return converter(this);
}
/**
* Convert to an ES6 Set.
* You must provide a function to convert the
* key to a string, number or boolean, because
* with other types equality is not correctly
* managed by JS.
* https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set
* https://esdiscuss.org/topic/maps-with-object-keys
*
* HashSet.of("a", "b").toJsSet(x=>x);
* => new Set(["a", "b"])
*/
toJsSet(keyConvert:(k:T)=>string): Set<string>;
toJsSet(keyConvert:(k:T)=>number): Set<number>;
toJsSet(keyConvert:(k:T)=>boolean): Set<boolean>;
toJsSet<K extends string|number|boolean>(keyConvert:(k:T)=>K): Set<K> {
return this.foldLeft(new Set<K>(), (sofar,cur) => sofar.add(keyConvert(cur)));
}
/**
* Two objects are equal if they represent the same value,
* regardless of whether they are the same object physically
* in memory.
*/
equals(other: HashSet<T>): boolean {
if (<any>other === this) {
return true;
}
const sz = this.hamt.size;
if (other === <EmptyHashSet<T>>emptyHashSet && sz === 0) {
// we could get that i'm not the empty map
// but my size is zero, after some filtering and such.
return true;
}
if (!other || !other.hamt) {
return false;
}
if (sz !== other.hamt.size) {
return false;
}
contractTrueEquality("HashSet.equals", this, other);
const keys: Array<T & WithEquality> = Array.from<T & WithEquality>(this.hamt.keys());
for (let k of keys) {
const hisVal: T & WithEquality|null|undefined = other.hamt.get(k);
if (hisVal === undefined) {
return false;
}
if (!areEqual(k, hisVal)) {
return false;
}
}
return true;
}
/**
* Get a number for that object. Two different values
* may get the same number, but one value must always get
* the same number. The formula can impact performance.
*/
hashCode(): number {
return this.hamt.fold(
(acc: number, value: T & WithEquality, key: T & WithEquality) =>
acc + getHashCode(key), 0);
}
/**
* Get a human-friendly string representation of that value.
*
* Also see [[HashSet.mkString]]
*/
toString(): string {
return "HashSet(" +
this.hamt.fold(
(acc: string[], value: T, key: T) =>
{acc.push(SeqHelpers.toStringHelper(key)); return acc;}, []).join(", ")
+ ")";
}
[inspect](): string {
return this.toString();
}
/**
* Joins elements of the collection by a separator.
* Example:
*
* HashSet.of(1,2,3).mkString(", ")
* => "1, 2, 3"
*
* (of course, order is not guaranteed)
*/
mkString(separator: string): string {
return this.hamt.fold(
(acc: string[], value: T, key: T) =>
{acc.push(SeqHelpers.toStringHelper(key, {quoteStrings: false})); return acc;}, []).join(separator);
}
}
// we need to override the empty hashmap
// because i don't know how to get the hash & keyset
// functions for the keys without a key value to get
// the functions from
class EmptyHashSet<T> extends HashSet<T> {
constructor() {
super({}); // we must override all the functions
}
add(elt: T & WithEquality): HashSet<T> {
contractTrueEquality("Error building a HashSet", elt);
if (!elt) {
// special case if we get null for the first element...
// less optimized variant because we don't know
// if we should use '===' or 'equals'
return new HashSet<T>(hamt.make({
hash: (v: T & HasEquals) => getHashCode(v),
keyEq: (a: T & HasEquals, b: T & HasEquals) => areEqual(a, b)
}).set(elt,elt));
}
// if the element is not null, save a if later by finding
// out right now whether we should call equals or ===
if (hasEquals(elt)) {
return new HashSet<T>(hamt.make({
hash: (v: T & HasEquals) => v.hashCode(),
keyEq: (a: T & HasEquals, b: T & HasEquals) => a.equals(b)
}).set(elt,elt));
}
return new HashSet<T>(hamt.make().set(elt,elt));
}
addAll(elts: Iterable<T & WithEquality>): HashSet<T> {
const it = elts[Symbol.iterator]();
let curItem = it.next();
if (curItem.done) {
return <EmptyHashSet<T>>emptyHashSet;
}
return this.add(curItem.value).addAll({[Symbol.iterator]: () => it});
}
contains(elt: T & WithEquality): boolean {
return false;
}
map<U>(mapper:(v:T)=>U&WithEquality): HashSet<U> {
return <EmptyHashSet<U>>emptyHashSet;
}
mapOption<U>(mapper:(v:T)=>Option<U&WithEquality>): HashSet<U> {
return <EmptyHashSet<U>>emptyHashSet;
}
forEach(fun:(x:T)=>void): HashSet<T> {
return this;
}
filter<U extends T>(fn:(v:T)=>v is U): HashSet<U>;
filter(predicate:(v:T)=>boolean): HashSet<T>;
filter(predicate:(v:T)=>boolean): HashSet<T> {
return this;
}
findAny(predicate:(v:T)=>boolean): Option<T> {
return Option.none();
}
foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
return zero;
}
toArray(sort?: SortOnSpec<T> | SortBySpec<T>): Array<T & WithEquality> {
return [];
}
toVector(): Vector<T & WithEquality> {
return Vector.empty<T&WithEquality>();
}
toLinkedList(): LinkedList<T & WithEquality> {
return LinkedList.empty<T&WithEquality>();
}
[Symbol.iterator](): Iterator<T> {
return { next: () => ({ done: true, value: <any>undefined }) };
}
length(): number {
return 0;
}
isEmpty(): boolean {
return true;
}
diff(elts: ISet<T&WithEquality>): HashSet<T> {
return this;
}
intersect(other: ISet<T&WithEquality>): HashSet<T> {
return this;
}
anyMatch(predicate:(v:T)=>boolean): boolean {
return false;
}
groupBy<C>(classifier: (v:T)=>C&WithEquality): HashMap<C,HashSet<T>> {
return HashMap.empty();
}
allMatch<U extends T>(predicate:(v:T)=>v is U): this is HashSet<U>;
allMatch(predicate:(v:T)=>boolean): boolean;
allMatch(predicate:(v:T)=>boolean): boolean {
return true;
}
partition<U extends T>(predicate:(v:T)=>v is U): [HashSet<U>,HashSet<Exclude<T,U>>];
partition(predicate:(x:T)=>boolean): [HashSet<T>,HashSet<T>];
partition<U extends T>(predicate:(v:T)=>boolean): [HashSet<U>,HashSet<any>] {
return [<any>this, <any>this];
}
remove(elt: T&WithEquality): HashSet<T> {
return this;
}
equals(other: HashSet<T>): boolean {
if (!other || !other.length) {
return false;
}
return <any>other === emptyHashSet || other.length() === 0;
}
hashCode(): number {
return 0;
}
toString(): string {
return "HashSet()";
}
mkString(separator: string): string {
return "";
}
}
const emptyHashSet = new EmptyHashSet<any>();
================================================
FILE: src/IMap.ts
================================================
import { WithEquality } from "./Comparison";
import { Option } from "./Option";
import { Value } from "./Value"
import { ISet } from "./ISet";
import { Vector } from "./Vector";
import { LinkedList } from "./LinkedList";
import { Foldable } from "./Foldable";
/**
* A generic interface for a dictionary, mapping keys to values.
* @param K the key type
* @param V the value type
*/
export interface IMap<K,V> extends Value, Iterable<[K,V]>, Foldable<[K,V]> {
/**
* Get a Set containing all the keys in the map
*/
keySet(): ISet<K>;
/**
* Get an iterable containing all the values in the map
* (can't return a set as we don't constrain map values
* to have equality in the generics type)
*/
valueIterable(): Iterable<V>;
/**
* Get the value for the key you give, if the key is present.
*/
get(k: K & WithEquality): Option<V>;
/**
* Add a new entry in the map. If there was entry with the same
* key, it will be overwritten.
* @param k the key
* @param v the value
*/
put(k: K & WithEquality, v: V): IMap<K,V>;
/**
* Return a new map with the key you give removed.
*/
remove(k: K&WithEquality): IMap<K,V>;
/**
* Add a new entry in the map; in case there was already an
* entry with the same key, the merge function will be invoked
* with the old and the new value to produce the value to take
* into account.
* @param k the key
* @param v the value
* @param merge a function to merge old and new values in case of conflict.
*/
putWithMerge(k: K & WithEquality, v: V, merge: (v1: V, v2: V) => V): IMap<K,V>;
/**
* Return a new map where each entry was transformed
* by the mapper function you give. You return key,value
* as pairs.
*/
map<K2,V2>(fn:(k:K&WithEquality, v:V)=>[K2&WithEquality,V2]): IMap<K2,V2>;
/**
* Return a new map where keys are the same as in this one,
* but values are transformed
* by the mapper function you give. You return key,value
* as pairs.
*/
mapValues<V2>(fn:(v:V)=>V2): IMap<K,V2>;
/**
* Call a function for element in the collection.
*/
forEach(fun:(x:[K,V])=>void):IMap<K,V>;
/**
* Calls the function you give for each item in the map,
* your function returns a map, all the maps are
* merged.
*/
flatMap<K2,V2>(fn:(k:K, v:V)=>Iterable<[K2&WithEquality,V2]>): IMap<K2,V2>;
/**
* Convert this map to a vector of key,value pairs.
* Note that Map is already an iterable of key,value pairs!
*/
toVector(): Vector<[K,V]>;
/**
* Convert this map to a List of key,value pairs.
* Note that Map is already an iterable of key,value pairs!
*/
toLinkedList(): LinkedList<[K,V]>;
/**
* Convert to array.
*/
toArray(): Array<[K,V]>;
/**
* Convert to a javascript object dictionary
* You must provide a function to convert the
* key to a string.
*
* HashMap.of<string,number>(["a",1],["b",2])
* .toObjectDictionary(x=>x);
* => {a:1,b:2}
*/
toObjectDictionary(keyConvert:(k:K)=>string): {[index:string]:V};
/**
* Convert to an ES6 Map.
* You must provide a function to convert the
* key to a string, number or boolean, because
* with other types equality is not correctly
* managed by JS.
* https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set
* https://esdiscuss.org/topic/maps-with-object-keys
*
* HashMap.of<string,number>(["a",1],["b",2])
* .toJsMap(x=>x);
* => new Map([["a",1], ["b",2]])
*/
toJsMap(keyConvert:(k:K)=>string): Map<string,V>;
toJsMap(keyConvert:(k:K)=>number): Map<number,V>;
toJsMap(keyConvert:(k:K)=>boolean): Map<boolean,V>;
/**
* number of items in the map
*/
length(): number;
/**
* true if the map is empty, false otherwise.
*/
isEmpty(): boolean;
/**
* Create a new map combining the entries of this map, and
* the other map you give. In case an entry from this map
* and the other map have the same key, the merge function
* will be invoked to get a combined value.
* @param other another map to merge with this one
* @param merge a merge function to combine two values
* in case two entries share the same key.
*/
mergeWith(other: Iterable<[K & WithEquality,V]>, merge:(v1: V, v2: V) => V): IMap<K,V>;
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:IMap<K,V>)=>U): U;
/**
* Returns true if the predicate returns true for all the
* elements in the collection.
*/
allMatch(predicate:(k:K,v:V)=>boolean): boolean;
/**
* Returns true if there the predicate returns true for any
* element in the collection.
*/
anyMatch(predicate:(k:K,v:V)=>boolean): boolean;
/**
* Returns true if the item is in the collection,
* false otherwise.
*/
contains(val: [K&WithEquality,V&WithEquality]): boolean;
/**
* Returns true if there is item with that key in the collection,
* false otherwise.
*/
containsKey(key: K&WithEquality): boolean;
/**
* Call a predicate for each element in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filter(predicate:(k:K,v:V)=>boolean): IMap<K,V>;
/**
* Call a predicate for each key in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filterKeys<U extends K>(fn:(v:K)=>v is U): IMap<U,V>
filterKeys(predicate:(k:K)=>boolean): IMap<K,V>;
/**
* Call a predicate for each value in the collection,
* build a new collection holding only the elements
* for which the predicate returned true.
*/
filterValues<U extends V>(fn:(v:V)=>v is U): IMap<K,U>
filterValues(predicate:(v:V)=>boolean): IMap<K,V>;
}
================================================
FILE: src/ISet.ts
================================================
import { WithEquality, Ordering, ToOrderable } from "./Comparison";
import { Value} from "./Value";
import { Collection } from "./Collection";
import { Vector } from "./Vector";
import { LinkedList } from "./LinkedList";
import { Option } from "./Option";
/**
* Ability to specify a sorting function.
* See [[Seq.sortOn]].
*
* You can give a lambda getting a sortable value (number or string) from your
* object, or a list of lambdas, and in the list you can also put {desc:lambda}
* items to request descending sorting
*
* `{sortOn: ToOrderable<T>|Array<ToOrderable<T>|{desc:ToOrderable<T>}>}`
*/
export type SortOnSpec<T> = {sortOn: ToOrderable<T>|Array<ToOrderable<T>|{desc:ToOrderable<T>}>};
/**
* Ability to specify a sorting function.
* See [[Seq.sortBy]].
*
* `{sortBy: (v1:T,v2:T)=>Ordering}`
*/
export type SortBySpec<T> = {sortBy: (v1:T,v2:T)=>Ordering};
/**
* @hidden
*/
export function isSortOnSpec<T>(sortSpec: SortOnSpec<T> | SortBySpec<T>): sortSpec is SortOnSpec<T> {
return (<any>sortSpec).sortOn !== undefined;
}
/**
* A generic interface for set-like implementations.
* @param T the item type
*/
export interface ISet<T> extends Collection<T> {
/**
* Returns the number of elements in the set.
*/
length(): number;
/**
* true if the set is empty, false otherwise.
*/
isEmpty(): boolean;
/**
* Add an element to this set.
*/
add(elt: T & WithEquality): ISet<T>;
/**
* Add multiple elements to this set.
*/
addAll(elts: Iterable<T & WithEquality>): ISet<T>;
/**
* Returns true if the element you give is present in
* the set, false otherwise.
*/
contains(elt: T & WithEquality): boolean;
/**
* Return a new collection where each element was transformed
* by the mapper function you give.
* The resulting set may be smaller than the source.
*/
map<U>(mapper:(v:T)=>U&WithEquality): ISet<U>;
/**
* Apply the mapper function on every element of this collection.
* The mapper function returns an Option; if the Option is a Some,
* the value it contains is added to the result Collection, if it's
* a None, the value is discarded.
*/
mapOption<U>(mapper:(v:T)=>Option<U&WithEquality>): ISet<U>;
/**
* Call a function for element in the collection.
*/
forEach(fun:(x:T)=>void):ISet<T>;
/**
* Calls the function you give for each item in the set,
* your function returns a set, all the sets are
* merged.
*/
flatMap<U>(mapper:(v:T)=>ISet<U&WithEquality>): ISet<U>;
/**
* Returns a new Set containing the difference
* between this set and the other Set passed as parameter.
* also see [[ISet.intersect]]
*/
diff(other: ISet<T&WithEquality>): ISet<T>;
/**
* Returns a new Set containing the intersection
* of this set and the other Set passed as parameter
* (the elements which are common to both sets)
* also see [[ISet.diff]]
*/
intersect(other: ISet<T&WithEquality>): ISet<T>;
/**
* Returns whether this set is a subset of the
* set you give as parameter (will return true
* also if both sets are equal)
*/
isSubsetOf(other: ISet<T&WithEquality>): boolean;
/**
* Returns a new set with all the elements of the current
* Set, minus the elements of the iterable you give as a parameter.
* If you call this function with a HashSet as parameter,
* rather call 'diff', as it'll be faster.
*/
removeAll(elts: Iterable<T&WithEquality>): ISet<T>;
/**
* Returns true if the predicate returns true for all the
* elements in the collection.
*/
allMatch<U extends T>(predicate:(v:T)=>v is U): this is ISet<U>;
allMatch(predicate:(v:T)=>boolean): boolean;
/**
* Returns true if there the predicate returns true for any
* element in the collection.
*/
anyMatch(predicate:(v:T)=>boolean): boolean;
/**
* Transform this value to another value type.
* Enables fluent-style programming by chaining calls.
*/
transform<U>(converter:(x:ISet<T>)=>U): U;
/**
* Converts this set to an array. Since a Set is not ordered
* and since this method returns a JS array, it can be awkward
* to get an array sorted in the way you'd like. So you can pass
* an optional sorting function too.
*
* HashSet.of(1,2,3).toArray().sort()
* => [1,2,3]
*
* HashSet.of(1,2,3).toArray({sortOn:x=>x})
* => [1,2,3]
*
* HashSet.of(1,2,3).toArray({sortBy:(x,y)=>x-y})
* => [1,2,3]
*
* You can also pass an array in sortOn, listing lambdas to
* several fields to sort by those fields, and also {desc:lambda}
* to sort by some fields descending.
*/
toArray(sort?: SortOnSpec<T> | SortBySpec<T>): Array<T & WithEquality>;
/**
* Converts this set to an vector
*/
toVector(): Vector<T & WithEquality>;
/**
* Converts this set to an list
*/
toLinkedList(): LinkedList<T & WithEquality>;
/**
* Convert to an ES6 Set.
* You must provide a function to convert the
* key to a string, number or boolean, because
* with other types equality is not correctly
* managed by JS.
* https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set
* https://esdiscuss.org/topic/maps-with-object-keys
*
* HashSet.of("a", "b").toJsSet(x=>x);
* => new Set(["a", "b"])
*/
toJsSet(keyConvert:(k:T)=>string): Set<string>;
toJsSet(keyConvert:(k:T)=>number): Set<number>;
toJsSet(keyConvert:(k:T)=>boolean): Set<boolean>;
}
================================================
FILE: src/Lazy.ts
================================================
import { inspect } from "./Value";
import { toStringHelper } from "./SeqHelpers";
/**
* Represent a lazily evaluated value. You give a function which
* will return a value; that function is only called when the value
* is requested from Lazy, but it will be computed at most once.
* If the value is requested again, the previously computed result
* will be returned: Lazy is memoizing.
*/
export class Lazy<T> {
private thunk: (()=>T)|undefined;
private value: T|undefined;
private constructor(thunk: ()=>T) {
this.thunk = thunk;
}
/**
* Build a Lazy from a computation returning a value.
* The computation will be called at most once.
*/
static of<T>(thunk: ()=>T) {
return new Lazy(thunk);
}
/**
* Evaluate the value, cache its value, and return it, or return the
* previously computed value.
*/
get(): T {
if (this.thunk) {
this.value = this.thunk();
this.thunk = undefined;
}
return <T>this.value;
}
/**
* Returns true if the computation underlying this Lazy was already
* performed, false otherwise.
*/
isEvaluated(): boolean {
return this.thunk === undefined;
}
/**
* Return a new lazy where the element was transformed
* by the mapper function you give.
*/
map<U>(mapper:(v:T)=>U): Lazy<U> {
return new Lazy(()=>mapper(this.get()));
}
/**
* Get a human-friendly string representation of that value.
*/
toString(): string {
return this.isEvaluated() ?
`Lazy(${toStringHelper(this.get())})` :
"Lazy(?)";
}
/**
* Used by the node REPL to display values.
* Most of the time should be the same as toString()
*/
[inspect](): string {
return this.toString();
}
}
================================================
FILE: src/LinkedList.ts
================================================
/**
* A sequence of values, organized in-memory as a strict linked list.
* Each element has an head (value) and a tail (the rest of the list).
*
* The code is organized through the class [[EmptyLinkedList]] (empty list
* or tail), the class [[ConsLinkedList]] (list value and pointer to next),
* and the type alias [[LinkedList]] (empty or cons).
*
* Finally, "static" functions on Option are arranged in the class
* [[LinkedListStatic]] and are accessed through the global constant LinkedList.
*
* Random access is expensive, appending is expensive, prepend or getting
* the tail of the list is very cheap.
* If you often need random access you should rather use [[Vector]].
* Avoid appending at the end of the list in a loop, prefer prepending and
* then reversing the list.
*
* Examples:
*
* LinkedList.of(1,2,3);
* LinkedList.of(1,2,3).map(x => x*2).last();
*/
import { Option, Some, None } from "./Option";
import { Vector } from "./Vector";
import { WithEquality, getHashCode,
areEqual, Ordering, ToOrderable } from "./Comparison";
import { contractTrueEquality } from "./Contract";
import { inspect } from "./Value";
import { HashMap } from "./HashMap";
import { HashSet } from "./HashSet";
import { Seq, IterableArray } from "./Seq";
import { Stream } from "./Stream";
import * as SeqHelpers from "./SeqHelpers";
/**
* Holds the "static methods" for [[LinkedList]]
*/
export class LinkedListStatic {
/**
* The empty stream
*/
empty<T>(): LinkedList<T> {
return <EmptyLinkedList<T>>emptyLinkedList;
}
/**
* Create a LinkedList with the elements you give.
*/
of<T>(elt: T, ...elts:T[]): ConsLinkedList<T>;
of<T>(...elts:T[]): LinkedList<T>;
of<T>(...elts:T[]): LinkedList<T> {
return LinkedList.ofIterable(elts);
}
/**
* Build a stream from any iterable, which means also
* an array for instance.
* @param T the item type
*/
ofIterable<T>(elts: Iterable<T>): LinkedList<T> {
const iterator = elts[Symbol.iterator]();
let curItem = iterator.next();
let result: LinkedList<T> = <EmptyLinkedList<T>>emptyLinkedList;
while (!curItem.done) {
result = new ConsLinkedList(curItem.value, result);
curItem = iterator.next();
}
return result.reverse();
}
/**
* Curried type guard for LinkedList.
* Sometimes needed also due to https://github.com/Microsoft/TypeScript/issues/20218
*
* Vector.of(LinkedList.of(1), LinkedList.empty<number>())
* .filter(LinkedList.isEmpty)
* => Vector.of(LinkedList.empty<number>())
*/
isEmpty<T>(l: LinkedList<T>): l is EmptyLinkedList<T> {
return l.isEmpty();
}
/**
* Curried type guard for LinkedList.
* Sometimes needed also due to https://github.com/Microsoft/TypeScript/issues/20218
*
* Vector.of(Stream.of(1), Stream.empty<number>())
* .filter(Stream.isNotEmpty)
* .map(s => s.head().get()+1)
* => Vector.of(2)
*/
i
gitextract_tam3khkl/
├── .circleci/
│ └── config.yml
├── .gitignore
├── .npmignore
├── LICENSE.TXT
├── README.md
├── benchmarks/
│ └── bench.ts
├── package.json
├── package.json.txt
├── scripts/
│ ├── make_doc.sh
│ ├── make_doc_extra/
│ │ ├── classes.ts
│ │ ├── globals.ts
│ │ ├── helpers.ts
│ │ ├── make_doc_extra.ts
│ │ └── make_doc_preprocess.ts
│ ├── prepublish.sh
│ └── with_header.sh
├── src/
│ ├── ChromeDevToolFormatters.ts
│ ├── Collection.ts
│ ├── Comparison.ts
│ ├── Contract.ts
│ ├── Either.ts
│ ├── Foldable.ts
│ ├── Function.ts
│ ├── Future.ts
│ ├── HashMap.ts
│ ├── HashSet.ts
│ ├── IMap.ts
│ ├── ISet.ts
│ ├── Lazy.ts
│ ├── LinkedList.ts
│ ├── Option.ts
│ ├── Predicate.ts
│ ├── Seq.ts
│ ├── SeqHelpers.ts
│ ├── Stream.ts
│ ├── Tuple2.ts
│ ├── Value.ts
│ ├── Vector.ts
│ └── index.ts
├── tests/
│ ├── Collection.ts
│ ├── Comments.ts
│ ├── Comparison.ts
│ ├── DocLinks.ts
│ ├── Either.ts
│ ├── Function.ts
│ ├── Future.ts
│ ├── HashMap.ts
│ ├── HashSet.ts
│ ├── Lazy.ts
│ ├── LinkedList.ts
│ ├── Option.ts
│ ├── Predicate.ts
│ ├── SampleData.ts
│ ├── Seq.ts
│ ├── Stream.ts
│ ├── TestHelpers.ts
│ ├── Tuple2.ts
│ └── Vector.ts
├── tsconfig.benchmarks.json
├── tsconfig.docgen.json
├── tsconfig.json
├── tsconfig.prepublish.json
├── tsconfig.test.json
└── www_demo/
└── index.html
SYMBOL INDEX (894 symbols across 36 files)
FILE: benchmarks/bench.ts
function getPrerequisites (line 23) | function getPrerequisites(length:number): Prerequisites {
type Prerequisites (line 62) | interface Prerequisites {
function _compare (line 83) | function _compare(preReqs: Prerequisites, items: Array<[string, (x:Prere...
function compare (line 94) | function compare(...items: Array<[string, (x:Prerequisites)=>any]>) {
function iterateOn (line 266) | function iterateOn<T>(coll: Iterable<T>) {
FILE: scripts/make_doc_extra/classes.ts
function putStaticMethodsOnTop (line 14) | function putStaticMethodsOnTop(parentTag: string, parentMarker: string, ...
function classPutStaticMethodsOnTop (line 54) | function classPutStaticMethodsOnTop(classfilename: string): void {
function putClassStaticMethodsOnTop (line 64) | function putClassStaticMethodsOnTop(): void {
FILE: scripts/make_doc_extra/globals.ts
constant CATEGORIES (line 8) | const CATEGORIES = Vector.of<[string,Vector<string>]>(
function getSectionHeader (line 20) | function getSectionHeader(sectionName:string): string {
function getSectionFooter (line 26) | function getSectionFooter(): string {
function groupGlobalsByCategory (line 43) | function groupGlobalsByCategory(): void {
FILE: scripts/make_doc_extra/helpers.ts
function requireNotNull (line 5) | function requireNotNull<T>(x:T|null): T {
function indent (line 12) | function indent(count: number): string {
type LineByIndent (line 17) | type LineByIndent = {contents:string,indent:number};
type LinesByIndent (line 18) | type LinesByIndent = Vector<LineByIndent>;
function fileGetLinesByIndent (line 20) | function fileGetLinesByIndent(fname: string): LinesByIndent {
function linesByIndentGetTagContents (line 32) | function linesByIndentGetTagContents(
function linesByIndentStr (line 50) | function linesByIndentStr(linesByIndent: LinesByIndent): string {
FILE: scripts/make_doc_extra/make_doc_preprocess.ts
type String (line 4) | interface String {
function makeModule (line 9) | function makeModule(moduleName:string, filename:string): void {
FILE: src/ChromeDevToolFormatters.ts
type ElementHandler (line 3) | interface ElementHandler {
function getWithToArrayBody (line 12) | function getWithToArrayBody(elt: any): any {
class VectorHandler (line 20) | class VectorHandler implements ElementHandler {
method isElement (line 21) | isElement(object:any): boolean {
method getHeader (line 25) | getHeader(object:any): any {
method hasBody (line 28) | hasBody(elt:any): boolean {
class StreamHandler (line 35) | class StreamHandler implements ElementHandler {
method isElement (line 36) | isElement(object:any): boolean {
method getHeader (line 39) | getHeader(object:any): any {
method hasBody (line 44) | hasBody(elt:any): boolean {
class ListHandler (line 50) | class ListHandler implements ElementHandler {
method isElement (line 51) | isElement(object:any): boolean {
method getHeader (line 54) | getHeader(object:any): any {
method hasBody (line 59) | hasBody(elt:any): boolean {
class HashSetHandler (line 65) | class HashSetHandler implements ElementHandler {
method isElement (line 66) | isElement(object:any): boolean {
method getHeader (line 69) | getHeader(object:any): any {
method hasBody (line 72) | hasBody(elt:any): boolean {
class HashMapHandler (line 78) | class HashMapHandler implements ElementHandler {
method isElement (line 79) | isElement(object:any): boolean {
method getHeader (line 82) | getHeader(object:any): any {
method hasBody (line 85) | hasBody(elt:any): boolean {
method getBody (line 88) | getBody(elt:any): any {
function getHandler (line 109) | function getHandler(object: any): ElementHandler|undefined {
FILE: src/Collection.ts
type Collection (line 7) | interface Collection<T> extends Value, Iterable<T>, Foldable<T> {
FILE: src/Comparison.ts
type ToOrderable (line 9) | type ToOrderable<T> = ((v:T)=>number) | ((v:T)=>string) | ((v:T)=>boolean);
type WithEquality (line 19) | type WithEquality
type HasEquals (line 29) | type HasEquals = {equals(other: any): boolean; hashCode(): number;};
function hasEquals (line 36) | function hasEquals(v: WithEquality): v is HasEquals {
function fieldsHashCode (line 55) | function fieldsHashCode(...fields: any[]): number {
function stringHashCode (line 68) | function stringHashCode(str: string): number {
function areEqual (line 84) | function areEqual(obj: any|null, obj2: any|null): boolean {
function getHashCode (line 103) | function getHashCode(obj: any|null): number {
function cachedHashString (line 131) | function cachedHashString(string: string) {
function smi (line 149) | function smi(i32: number): number {
constant STRING_HASH_CACHE_MIN_STRLEN (line 153) | const STRING_HASH_CACHE_MIN_STRLEN = 16;
constant STRING_HASH_CACHE_MAX_SIZE (line 154) | const STRING_HASH_CACHE_MAX_SIZE = 255;
constant STRING_HASH_CACHE_SIZE (line 155) | let STRING_HASH_CACHE_SIZE = 0;
function hasTrueEquality (line 161) | function hasTrueEquality(val: any): Option<boolean> {
type Ordering (line 181) | const enum Ordering {
type TypeGuard (line 208) | type TypeGuard<T,U extends T> = (x: T) => x is U;
function typeGuard (line 226) | function typeGuard<T,U extends T>(predicate:(x:T)=>boolean,
function instanceOf (line 248) | function instanceOf<T>(ctor: new(...args: any[]) => T): TypeGuard<any,T> {
function typeOf (line 274) | function typeOf(typ: string): TypeGuard<any,any> {
FILE: src/Contract.ts
function setContractViolationAction (line 22) | function setContractViolationAction(action: (msg:string)=>void) {
function reportContractViolation (line 29) | function reportContractViolation(msg: string): void {
function contractTrueEquality (line 36) | function contractTrueEquality(context: string, ...vals: Array<any>) {
FILE: src/Either.ts
class EitherStatic (line 33) | class EitherStatic {
method left (line 37) | left<L,R>(val: L): Either<L,R> {
method right (line 44) | right<L,R>(val: R): Either<L,R> {
method isLeft (line 57) | isLeft<L,R>(e: Either<L,R>): e is Left<L,R> {
method isRight (line 70) | isRight<L,R>(e: Either<L,R>): e is Right<L,R> {
method sequence (line 93) | sequence<L,R>(elts:Iterable<Either<L,R>>): Either<L,Vector<R>> {
method traverse (line 116) | traverse<T,L,R>(elts:Iterable<T>, fn: (x:T)=>Either<L,R>): Either<L,Ve...
method sequenceAcc (line 149) | sequenceAcc<L,R>(elts:Iterable<Either<L,R>>): Either<Vector<L>,Vector<...
method liftA2 (line 183) | liftA2<R1,R2,L,V>(fn:(v1:R1,v2:R2)=>V, leftWitness?: L) : (p1:Either<L...
method liftAp (line 215) | liftAp<L,A,B>(fn:(x:A)=>B, leftWitness?: L): (x: {[K in keyof A]: Eith...
method liftApAcc (line 259) | liftApAcc<L,A,B>(fn:(x:A)=>B, leftWitness?: L): (x: {[K in keyof A]: E...
method lift (line 305) | lift<T extends any[],L,U>(fn: (...args: T)=>U, witness?: L): (...args:...
method try_ (line 345) | try_<L,T>(fn:()=>T, witness?: L): Either<L,T> {
type Either (line 361) | type Either<L,R> = Left<L,R> | Right<L,R>;
class Left (line 370) | class Left<L,R> implements Value {
method constructor (line 371) | constructor(private value: L) {}
method isLeft (line 381) | isLeft(): this is Left<L,R> {
method isRight (line 388) | isRight(): this is Right<L,R> {
method contains (line 395) | contains(val: R&WithEquality): boolean {
method map (line 403) | map<U>(fn: (x:R)=>U): Either<L,U> {
method flatMap (line 413) | flatMap<U>(fn: (x:R)=>Either<L,U>): Either<L,U> {
method mapLeft (line 422) | mapLeft<U>(fn: (x:L)=>U): Either<U,R> {
method bimap (line 430) | bimap<S,T>(fnL: (x:L)=>S,fnR: (x:R)=>T): Either<S,T> {
method filter (line 446) | filter(p: (x:R)=>boolean, filterVal: (x:R)=>L): Either<L,R> {
method orElse (line 454) | orElse(other: Either<L,R>): Either<L,R> {
method recoverWith (line 464) | recoverWith(recoveryFn: (left:L)=>Either<L, R>): Either<L, R> {
method ifRight (line 472) | ifRight(fn: (x:R)=>void): Either<L,R> {
method ifLeft (line 480) | ifLeft(fn: (x:L)=>void): Either<L,R> {
method match (line 496) | match<U>(cases: {Left: (v:L)=>U, Right: (v:R)=>U}): U {
method getOrThrow (line 506) | getOrThrow(errorInfo?: Error|string): R {
method getOrElse (line 517) | getOrElse(other: R): R {
method getLeft (line 526) | getLeft(): L {
method getLeftOrThrow (line 536) | getLeftOrThrow(message?: string): L {
method getLeftOrElse (line 544) | getLeftOrElse(other: L): L {
method toOption (line 552) | toOption(): Option<R> {
method toVector (line 561) | toVector(): Vector<R> {
method toLinkedList (line 570) | toLinkedList(): LinkedList<R> {
method transform (line 578) | transform<U>(converter:(x:Either<L,R>)=>U): U {
method hasTrueEquality (line 582) | hasTrueEquality(): boolean {
method hashCode (line 593) | hashCode(): number {
method equals (line 602) | equals(other: Either<L&WithEquality,R&WithEquality>): boolean {
method toString (line 617) | toString(): string {
method [inspect] (line 624) | [inspect](): string {
class Right (line 636) | class Right<L,R> implements Value {
method constructor (line 637) | constructor(private value: R) {}
method isLeft (line 647) | isLeft(): this is Left<L,R> {
method isRight (line 654) | isRight(): this is Right<L,R> {
method contains (line 661) | contains(val: R&WithEquality): boolean {
method map (line 669) | map<U>(fn: (x:R)=>U): Either<L,U> {
method flatMap (line 679) | flatMap<U>(fn: (x:R)=>Either<L,U>): Either<L,U> {
method mapLeft (line 688) | mapLeft<U>(fn: (x:L)=>U): Either<U,R> {
method bimap (line 696) | bimap<S,T>(fnL: (x:L)=>S,fnR: (x:R)=>T): Either<S,T> {
method filter (line 712) | filter(p: (x:R)=>boolean, filterVal: (x:R)=>L): Either<L,R> {
method orElse (line 723) | orElse(other: Either<L,R>): Either<L,R> {
method recoverWith (line 733) | recoverWith(recoveryFn: (left:L)=>Either<L, R>): Either<L, R> {
method ifRight (line 741) | ifRight(fn: (x:R)=>void): Either<L,R> {
method ifLeft (line 750) | ifLeft(fn: (x:L)=>void): Either<L,R> {
method match (line 765) | match<U>(cases: {Left: (v:L)=>U, Right: (v:R)=>U}): U {
method get (line 774) | get(): R {
method getOrThrow (line 784) | getOrThrow(errorInfo?: Error|string): R {
method getOrElse (line 792) | getOrElse(other: R): R {
method getLeftOrThrow (line 802) | getLeftOrThrow(message?: string): L {
method getLeftOrElse (line 810) | getLeftOrElse(other: L): L {
method toOption (line 818) | toOption(): Option<R> {
method toVector (line 827) | toVector(): Vector<R> {
method toLinkedList (line 836) | toLinkedList(): LinkedList<R> {
method transform (line 844) | transform<U>(converter:(x:Either<L,R>)=>U): U {
method hasTrueEquality (line 848) | hasTrueEquality(): boolean {
method hashCode (line 859) | hashCode(): number {
method equals (line 868) | equals(other: Either<L&WithEquality,R&WithEquality>): boolean {
method toString (line 883) | toString(): string {
method [inspect] (line 890) | [inspect](): string {
FILE: src/Foldable.ts
type Foldable (line 3) | interface Foldable<T> {
FILE: src/Function.ts
type Function0 (line 29) | interface Function0<R> {
type Function1 (line 51) | interface Function1<T,U> {
type Function2 (line 79) | interface Function2<T1,T2,R> {
type Function3 (line 136) | interface Function3<T1,T2,T3,R> {
type Function4 (line 199) | interface Function4<T1,T2,T3,T4,R> {
type Function5 (line 273) | interface Function5<T1,T2,T3,T4,T5,R> {
class Function0Static (line 353) | class Function0Static {
method constant (line 360) | constant<R>(val:R): Function0<R> {
method of (line 368) | of<R>(fn:()=>R): Function0<R> {
class Function1Static (line 388) | class Function1Static {
method id (line 393) | id<T>(): Function1<T,T> {
method constant (line 402) | constant<U,T>(val:T): Function1<U,T> {
method of (line 410) | of<T,U>(fn:(x:T)=>U): Function1<T,U> {
class Function2Static (line 431) | class Function2Static {
method constant (line 437) | constant<T1,T2,R>(val:R): Function2<T1,T2,R> {
method of (line 445) | of<T1,T2,R>(fn:(x:T1,y:T2)=>R): Function2<T1,T2,R> {
class Function3Static (line 469) | class Function3Static {
method constant (line 475) | constant<T1,T2,T3,R>(val:R): Function3<T1,T2,T3,R> {
method of (line 483) | of<T1,T2,T3,R>(fn:(x:T1,y:T2,z:T3)=>R): Function3<T1,T2,T3,R> {
class Function4Static (line 508) | class Function4Static {
method constant (line 515) | constant<T1,T2,T3,T4,R>(val:R): Function4<T1,T2,T3,T4,R> {
method of (line 523) | of<T1,T2,T3,T4,R>(fn:(x:T1,y:T2,z:T3,a:T4)=>R): Function4<T1,T2,T3,T4,...
class Function5Static (line 551) | class Function5Static {
method constant (line 557) | constant<T1,T2,T3,T4,T5,R>(val:R): Function5<T1,T2,T3,T4,T5,R> {
method of (line 565) | of<T1,T2,T3,T4,T5,R>(fn:(x:T1,y:T2,z:T3,a:T4,b:T5)=>R): Function5<T1,T...
FILE: src/Future.ts
class Future (line 16) | class Future<T> {
method constructor (line 23) | private constructor(private promise: Promise<T[]>) { }
method ofPromiseCtor (line 33) | static ofPromiseCtor<T>(executor: (resolve:(x:T)=>void, reject: (x:any...
method of (line 40) | static of<T>(promise: Promise<T>): Future<T> {
method ofCallback (line 49) | static ofCallback<T>(fn: (cb:(err:any, val:T)=>void)=>void): Future<T> {
method ok (line 62) | static ok<T>(val:T): Future<T> {
method failed (line 69) | static failed<T>(reason: any): Future<T> {
method do (line 85) | static do<T>(fn: ()=>Promise<T>): Future<T> {
method then (line 95) | then<TResult1 = T, TResult2 = never>(
method toPromise (line 104) | toPromise(): Promise<T> {
method firstCompletedOf (line 116) | static firstCompletedOf<T>(elts: Iterable<Future<T>>): Future<T> {
method firstSuccessfulOf (line 128) | static firstSuccessfulOf<T>(elts: Iterable<Future<T>>): Future<T> {
method sequence (line 156) | static sequence<T>(elts: Iterable<Future<T>>): Future<Vector<T>> {
method traverse (line 175) | static traverse<T,U>(elts: Iterable<T>, fn: (x:T)=>Future<U>,
method find (line 225) | static find<T>(elts: Iterable<Future<T>>, p: (x: T) => boolean): Futur...
method liftAp (line 268) | static liftAp<A,B>(fn:(x:A)=>B): (x: {[K in keyof A]: Future<A[K]>;}) ...
method liftA2 (line 293) | static liftA2<R1,R2,V>(fn:(v1:R1,v2:R2)=>V) : (p1:Future<R1>, p2:Futur...
method lift (line 301) | static lift<T extends any[],U>(fn: (...args: T)=>Promise<U>): (...args...
method map (line 311) | map<U>(fn: (x:T)=>U): Future<U> {
method flatMap (line 324) | flatMap<U>(fn: (x:T)=>Future<U>): Future<U> {
method mapFailure (line 332) | mapFailure(fn: (x:any)=>any): Future<T> {
method onFailure (line 341) | onFailure(fn: (x:any)=>void): Future<T> {
method onSuccess (line 351) | onSuccess(fn: (x:T)=>void): Future<T> {
method onComplete (line 365) | onComplete(fn: (x:Either<any,T>)=>void): Future<T> {
method filter (line 382) | filter(p: (x:T)=>boolean, ifFail: (x:T)=>any): Future<T> {
method recoverWith (line 393) | recoverWith(f: (err:any)=>Future<T>): Future<T> {
method transform (line 401) | transform<U>(fn: (x:Future<T>)=>U): U {
FILE: src/HashMap.ts
class HashMap (line 27) | class HashMap<K,V> implements IMap<K,V> {
method constructor (line 32) | protected constructor(private hamt: any) {}
method empty (line 39) | static empty<K,V>(): HashMap<K,V> {
method of (line 49) | static of<K,V>(...entries: Array<[K&WithEquality, V]>): HashMap<K,V> {
method ofIterable (line 58) | static ofIterable<K,V>(entries: Iterable<[K&WithEquality, V]>): HashMa...
method ofObjectDictionary (line 86) | static ofObjectDictionary<V>(object: {[index:string]: V|undefined}): H...
method isEmpty (line 111) | static isEmpty<K,V>(v: HashMap<K,V>): boolean {
method isNotEmpty (line 122) | static isNotEmpty<K,V>(v: HashMap<K,V>): boolean {
method get (line 129) | get(k: K & WithEquality): Option<V> {
method hasTrueEquality (line 143) | hasTrueEquality(): boolean {
method put (line 160) | put(k: K & WithEquality, v: V): HashMap<K,V> {
method remove (line 167) | remove(k: K&WithEquality): HashMap<K,V> {
method putWithMerge (line 184) | putWithMerge(k: K & WithEquality, v: V, merge: (v1: V, v2: V) => V): H...
method length (line 196) | length(): number {
method single (line 204) | single(): Option<[K,V]> {
method isEmpty (line 213) | isEmpty(): boolean {
method keySet (line 220) | keySet(): HashSet<K> {
method valueIterable (line 229) | valueIterable(): Iterable<V> {
method mergeWith (line 249) | mergeWith(elts: Iterable<[K & WithEquality,V]>, merge:(v1: V, v2: V) =...
method map (line 265) | map<K2,V2>(fn:(k:K&WithEquality, v:V)=>[K2&WithEquality,V2]): HashMap<...
method mapValues (line 279) | mapValues<V2>(fn:(v:V)=>V2): HashMap<K,V2> {
method forEach (line 288) | forEach(fun:(x:[K,V])=>void): HashMap<K,V> {
method flatMap (line 303) | flatMap<K2,V2>(fn:(k:K, v:V)=>Iterable<[K2&WithEquality,V2]>): HashMap...
method allMatch (line 312) | allMatch(predicate:(k:K,v:V)=>boolean): boolean {
method anyMatch (line 328) | anyMatch(predicate:(k:K,v:V)=>boolean): boolean {
method contains (line 344) | contains(val: [K&WithEquality,V&WithEquality]): boolean {
method containsKey (line 358) | containsKey(key: K&WithEquality): boolean {
method filter (line 367) | filter(predicate:(k:K,v:V)=>boolean): HashMap<K,V> {
method findAny (line 396) | findAny(predicate:(k:K,v:V)=>boolean): Option<[K,V]> {
method filterKeys (line 418) | filterKeys(predicate:(k:K)=>boolean): HashMap<K,V> {
method filterValues (line 432) | filterValues(predicate:(k:V)=>boolean): HashMap<K,V> {
method fold (line 447) | fold(zero:[K,V], fn:(v1:[K,V],v2:[K,V])=>[K,V]): [K,V] {
method foldLeft (line 467) | foldLeft<U>(zero: U, fn:(soFar:U,cur:[K,V])=>U): U {
method foldRight (line 489) | foldRight<U>(zero: U, fn:(cur:[K,V], soFar:U)=>U): U {
method reduce (line 499) | reduce(combine: (v1:[K,V],v2:[K,V])=>[K,V]): Option<[K,V]> {
method toArray (line 507) | toArray(): Array<[K,V]> {
method toVector (line 517) | toVector(): Vector<[K,V]> {
method toLinkedList (line 527) | toLinkedList(): LinkedList<[K,V]> {
method toObjectDictionary (line 540) | toObjectDictionary(keyConvert:(k:K)=>string): {[index:string]:V} {
method toJsMap (line 563) | toJsMap<K2 extends number|string|boolean>(keyConvert:(k:K)=>K2): Map<K...
method transform (line 573) | transform<U>(converter:(x:HashMap<K,V>)=>U): U {
method equals (line 582) | equals(other: IMap<K&WithEquality,V&WithEquality>): boolean {
method hashCode (line 618) | hashCode(): number {
method toString (line 644) | toString(): string {
method [inspect] (line 654) | [inspect](): string {
method [Symbol.iterator] (line 136) | [Symbol.iterator](): Iterator<[K,V]> {
class EmptyHashMap (line 663) | class EmptyHashMap<K,V> extends HashMap<K,V> {
method constructor (line 665) | constructor() {
method get (line 669) | get(k: K & WithEquality): Option<V> {
method put (line 677) | put(k: K & WithEquality, v: V): HashMap<K,V> {
method remove (line 688) | remove(k: K&WithEquality): HashMap<K,V> {
method hasTrueEquality (line 692) | hasTrueEquality(): boolean {
method putWithMerge (line 696) | putWithMerge(k: K & WithEquality, v: V, merge: (v1: V, v2: V) => V): H...
method length (line 700) | length(): number {
method single (line 708) | single(): Option<[K,V]> {
method isEmpty (line 712) | isEmpty(): boolean {
method keySet (line 716) | keySet(): HashSet<K> {
method valueIterable (line 720) | valueIterable(): Iterable<V> {
method mergeWith (line 735) | mergeWith(other: Iterable<[K & WithEquality,V]>, merge:(v1: V, v2: V) ...
method map (line 739) | map<K2,V2>(fn:(k:K&WithEquality, v:V)=>[K2&WithEquality,V2]): HashMap<...
method mapValues (line 743) | mapValues<V2>(fn:(v:V)=>V2): HashMap<K,V2> {
method forEach (line 747) | forEach(fun:(x:[K,V])=>void): HashMap<K,V> {
method allMatch (line 751) | allMatch(predicate:(k:K,v:V)=>boolean): boolean {
method anyMatch (line 755) | anyMatch(predicate:(k:K,v:V)=>boolean): boolean {
method contains (line 759) | contains(val: [K&WithEquality,V&WithEquality]): boolean {
method containsKey (line 763) | containsKey(key: K&WithEquality) : boolean {
method filter (line 767) | filter(predicate:(k:K,v:V)=>boolean): HashMap<K,V> {
method findAny (line 771) | findAny(predicate:(k:K,v:V)=>boolean): Option<[K,V]> {
method foldLeft (line 775) | foldLeft<U>(zero: U, fn:(soFar:U,cur:[K,V])=>U): U {
method toArray (line 779) | toArray(): Array<[K,V]> {
method toVector (line 783) | toVector(): Vector<[K,V]> {
method toLinkedList (line 787) | toLinkedList(): LinkedList<[K,V]> {
method equals (line 791) | equals(other: IMap<K&WithEquality,V&WithEquality>): boolean {
method hashCode (line 798) | hashCode(): number {
method toString (line 802) | toString(): string {
method [Symbol.iterator] (line 673) | [Symbol.iterator](): Iterator<[K,V]> {
FILE: src/HashSet.ts
class HashSet (line 19) | class HashSet<T> implements ISet<T> {
method constructor (line 24) | protected constructor(private hamt: any) {}
method empty (line 30) | static empty<T>(): HashSet<T> {
method ofIterable (line 39) | static ofIterable<T>(elts: Iterable<T & WithEquality>): HashSet<T> {
method of (line 47) | static of<T>(...arr: Array<T & WithEquality>): HashSet<T> {
method isEmpty (line 58) | static isEmpty<T>(v: HashSet<T>): boolean {
method isNotEmpty (line 69) | static isNotEmpty<T>(v: HashSet<T>): boolean {
method add (line 83) | add(elt: T & WithEquality): HashSet<T> {
method addAllArray (line 87) | private addAllArray(elts: Array<T&WithEquality>): HashSet<T> {
method addAll (line 101) | addAll(elts: Iterable<T & WithEquality>): HashSet<T> {
method contains (line 124) | contains(elt: T & WithEquality): boolean {
method map (line 133) | map<U>(mapper:(v:T)=>U&WithEquality): HashSet<U> {
method mapOption (line 150) | mapOption<U>(mapper:(v:T)=>Option<U&WithEquality>): HashSet<U> {
method forEach (line 161) | forEach(fun:(x:T)=>void): HashSet<T> {
method flatMap (line 176) | flatMap<U>(mapper:(v:T)=>HashSet<U&WithEquality>): HashSet<U> {
method filter (line 188) | filter(predicate:(v:T)=>boolean): HashSet<T> {
method findAny (line 215) | findAny(predicate:(v:T)=>boolean): Option<T> {
method fold (line 237) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 256) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method foldRight (line 277) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method toArray (line 300) | toArray(sort?: SortOnSpec<T> | SortBySpec<T>): Array<T & WithEquality> {
method toVector (line 316) | toVector(): Vector<T & WithEquality> {
method toLinkedList (line 323) | toLinkedList(): LinkedList<T & WithEquality> {
method length (line 330) | length(): number {
method single (line 338) | single(): Option<T> {
method isEmpty (line 347) | isEmpty(): boolean {
method diff (line 356) | diff(elts: ISet<T&WithEquality>): HashSet<T> {
method intersect (line 368) | intersect(other: ISet<T&WithEquality>): HashSet<T> {
method isSubsetOf (line 374) | isSubsetOf(other: ISet<T&WithEquality>): boolean {
method remove (line 382) | remove(elt: T&WithEquality): HashSet<T> {
method removeAll (line 392) | removeAll(elts: Iterable<T&WithEquality>): HashSet<T> {
method allMatch (line 402) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 418) | anyMatch(predicate:(v:T)=>boolean): boolean {
method groupBy (line 438) | groupBy<C>(classifier: (v:T)=>C&WithEquality): HashMap<C,HashSet<T>> {
method arrangeBy (line 466) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method partition (line 482) | partition(predicate:(v:T)=>boolean): [HashSet<T>,HashSet<T>] {
method reduce (line 508) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 518) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 529) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 539) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 550) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 562) | sumOn(getNumber: (v:T)=>number): number {
method transform (line 570) | transform<U>(converter:(x:HashSet<T>)=>U): U {
method toJsSet (line 589) | toJsSet<K extends string|number|boolean>(keyConvert:(k:T)=>K): Set<K> {
method equals (line 598) | equals(other: HashSet<T>): boolean {
method hashCode (line 633) | hashCode(): number {
method toString (line 644) | toString(): string {
method [inspect] (line 652) | [inspect](): string {
method mkString (line 665) | mkString(separator: string): string {
method [Symbol.iterator] (line 76) | [Symbol.iterator](): Iterator<T> {
class EmptyHashSet (line 676) | class EmptyHashSet<T> extends HashSet<T> {
method constructor (line 678) | constructor() {
method add (line 682) | add(elt: T & WithEquality): HashSet<T> {
method addAll (line 704) | addAll(elts: Iterable<T & WithEquality>): HashSet<T> {
method contains (line 713) | contains(elt: T & WithEquality): boolean {
method map (line 717) | map<U>(mapper:(v:T)=>U&WithEquality): HashSet<U> {
method mapOption (line 721) | mapOption<U>(mapper:(v:T)=>Option<U&WithEquality>): HashSet<U> {
method forEach (line 725) | forEach(fun:(x:T)=>void): HashSet<T> {
method filter (line 731) | filter(predicate:(v:T)=>boolean): HashSet<T> {
method findAny (line 735) | findAny(predicate:(v:T)=>boolean): Option<T> {
method foldLeft (line 739) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method toArray (line 743) | toArray(sort?: SortOnSpec<T> | SortBySpec<T>): Array<T & WithEquality> {
method toVector (line 747) | toVector(): Vector<T & WithEquality> {
method toLinkedList (line 751) | toLinkedList(): LinkedList<T & WithEquality> {
method length (line 759) | length(): number {
method isEmpty (line 763) | isEmpty(): boolean {
method diff (line 767) | diff(elts: ISet<T&WithEquality>): HashSet<T> {
method intersect (line 771) | intersect(other: ISet<T&WithEquality>): HashSet<T> {
method anyMatch (line 775) | anyMatch(predicate:(v:T)=>boolean): boolean {
method groupBy (line 779) | groupBy<C>(classifier: (v:T)=>C&WithEquality): HashMap<C,HashSet<T>> {
method allMatch (line 785) | allMatch(predicate:(v:T)=>boolean): boolean {
method partition (line 791) | partition<U extends T>(predicate:(v:T)=>boolean): [HashSet<U>,HashSet<...
method remove (line 795) | remove(elt: T&WithEquality): HashSet<T> {
method equals (line 799) | equals(other: HashSet<T>): boolean {
method hashCode (line 806) | hashCode(): number {
method toString (line 810) | toString(): string {
method mkString (line 814) | mkString(separator: string): string {
method [Symbol.iterator] (line 755) | [Symbol.iterator](): Iterator<T> {
FILE: src/IMap.ts
type IMap (line 14) | interface IMap<K,V> extends Value, Iterable<[K,V]>, Foldable<[K,V]> {
FILE: src/ISet.ts
type SortOnSpec (line 18) | type SortOnSpec<T> = {sortOn: ToOrderable<T>|Array<ToOrderable<T>|{desc:...
type SortBySpec (line 26) | type SortBySpec<T> = {sortBy: (v1:T,v2:T)=>Ordering};
function isSortOnSpec (line 31) | function isSortOnSpec<T>(sortSpec: SortOnSpec<T> | SortBySpec<T>): sortS...
type ISet (line 39) | interface ISet<T> extends Collection<T> {
FILE: src/Lazy.ts
class Lazy (line 11) | class Lazy<T> {
method constructor (line 16) | private constructor(thunk: ()=>T) {
method of (line 24) | static of<T>(thunk: ()=>T) {
method get (line 32) | get(): T {
method isEvaluated (line 44) | isEvaluated(): boolean {
method map (line 52) | map<U>(mapper:(v:T)=>U): Lazy<U> {
method toString (line 59) | toString(): string {
method [inspect] (line 69) | [inspect](): string {
FILE: src/LinkedList.ts
class LinkedListStatic (line 38) | class LinkedListStatic {
method empty (line 42) | empty<T>(): LinkedList<T> {
method of (line 51) | of<T>(...elts:T[]): LinkedList<T> {
method ofIterable (line 60) | ofIterable<T>(elts: Iterable<T>): LinkedList<T> {
method isEmpty (line 79) | isEmpty<T>(l: LinkedList<T>): l is EmptyLinkedList<T> {
method isNotEmpty (line 92) | isNotEmpty<T>(l: LinkedList<T>): l is ConsLinkedList<T> {
method unfoldRight (line 110) | unfoldRight<T,U>(seed: T, fn: (x:T)=>Option<[U,T]>): LinkedList<U> {
method zip (line 138) | zip<A extends any[]>(...iterables: IterableArray<A>): LinkedList<A> {
type LinkedList (line 161) | type LinkedList<T> = EmptyLinkedList<T> | ConsLinkedList<T>;
class EmptyLinkedList (line 170) | class EmptyLinkedList<T> implements Seq<T> {
method hasTrueEquality (line 175) | hasTrueEquality(): boolean {
method asLinkedList (line 202) | asLinkedList(): LinkedList<T> {
method length (line 209) | length(): number {
method single (line 217) | single(): Option<T> {
method isEmpty (line 224) | isEmpty(): this is EmptyLinkedList<T> {
method head (line 232) | head(): None<T> {
method tail (line 240) | tail(): Option<LinkedList<T>> {
method last (line 249) | last(): Option<T> {
method get (line 262) | get(idx: number): Option<T> {
method find (line 271) | find(predicate:(v:T)=>boolean): Option<T> {
method contains (line 279) | contains(v:T&WithEquality): boolean {
method take (line 287) | take(n: number): LinkedList<T> {
method takeWhile (line 295) | takeWhile(predicate: (x:T)=>boolean): LinkedList<T> {
method takeRightWhile (line 307) | takeRightWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method drop (line 317) | drop(n:number): LinkedList<T> {
method dropWhile (line 326) | dropWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method dropRight (line 336) | dropRight(n:number): LinkedList<T> {
method dropRightWhile (line 345) | dropRightWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method fold (line 359) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 377) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method foldRight (line 395) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method zip (line 413) | zip<U>(other: Iterable<U>): LinkedList<[T,U]> {
method zipWithIndex (line 425) | zipWithIndex(): LinkedList<[T,number]> {
method reverse (line 435) | reverse(): LinkedList<T> {
method span (line 448) | span(predicate:(x:T)=>boolean): [LinkedList<T>,LinkedList<T>] {
method splitAt (line 458) | splitAt(index:number): [LinkedList<T>,LinkedList<T>] {
method partition (line 474) | partition<U extends T>(predicate:(v:T)=>boolean): [LinkedList<U>,Linke...
method groupBy (line 486) | groupBy<C>(classifier: (v:T)=>C & WithEquality): HashMap<C,LinkedList<...
method arrangeBy (line 496) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method shuffle (line 503) | shuffle(): LinkedList<T> {
method append (line 512) | append(v:T): LinkedList<T> {
method appendAll (line 519) | appendAll(elts:Iterable<T>): LinkedList<T> {
method removeAll (line 529) | removeAll(elts:Iterable<T&WithEquality>): LinkedList<T> {
method removeFirst (line 537) | removeFirst(predicate: (x:T)=>boolean): LinkedList<T> {
method prepend (line 544) | prepend(elt: T): LinkedList<T> {
method prependAll (line 551) | prependAll(elt: Iterable<T>): LinkedList<T> {
method map (line 559) | map<U>(mapper:(v:T)=>U): LinkedList<U> {
method mapOption (line 573) | mapOption<U>(mapper:(v:T)=>Option<U>): LinkedList<U> {
method flatMap (line 583) | flatMap<U>(mapper:(v:T)=>LinkedList<U>): LinkedList<U> {
method allMatch (line 593) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 601) | anyMatch(predicate:(v:T)=>boolean): boolean {
method filter (line 612) | filter(predicate:(v:T)=>boolean): LinkedList<T> {
method sortBy (line 627) | sortBy(compare: (v1:T,v2:T)=>Ordering): LinkedList<T> {
method sortOn (line 647) | sortOn(...getKeys: Array<ToOrderable<T>|{desc:ToOrderable<T>}>): Linke...
method distinctBy (line 658) | distinctBy<U>(keyExtractor: (x:T)=>U&WithEquality): LinkedList<T> {
method forEach (line 665) | forEach(fn: (v:T)=>void): LinkedList<T> {
method reduce (line 675) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 685) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 699) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 709) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 723) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 735) | sumOn(getNumber: (v:T)=>number): number {
method sliding (line 746) | sliding(count:number): Stream<ConsLinkedList<T>> {
method scanLeft (line 759) | scanLeft<U>(init:U, fn:(soFar:U,cur:T)=>U): LinkedList<U> {
method scanRight (line 772) | scanRight<U>(init:U, fn:(cur:T,soFar:U)=>U): LinkedList<U> {
method mkString (line 783) | mkString(separator: string): string {
method toArray (line 791) | toArray(): T[] {
method toVector (line 799) | toVector(): Vector<T> {
method toMap (line 813) | toMap<K,V>(converter:(x:T)=>[K & WithEquality,V]): HashMap<K,V> {
method toSet (line 825) | toSet<K>(converter:(x:T)=>K&WithEquality): HashSet<K> {
method transform (line 833) | transform<U>(converter:(x:LinkedList<T>)=>U): U {
method equals (line 842) | equals(other: LinkedList<T&WithEquality>): boolean {
method hashCode (line 854) | hashCode(): number {
method [inspect] (line 858) | [inspect](): string {
method toString (line 867) | toString(): string {
method [Symbol.iterator] (line 182) | [Symbol.iterator](): Iterator<T> {
class ConsLinkedList (line 880) | class ConsLinkedList<T> implements Seq<T> {
method constructor (line 890) | public constructor(protected value: T, protected _tail: LinkedList<T>) {}
method hasTrueEquality (line 895) | hasTrueEquality(): boolean {
method asLinkedList (line 903) | asLinkedList(): LinkedList<T> {
method length (line 927) | length(): number {
method single (line 935) | single(): Option<T> {
method isEmpty (line 944) | isEmpty(): this is EmptyLinkedList<T> {
method head (line 952) | head(): Some<T> {
method tail (line 960) | tail(): Some<LinkedList<T>> {
method last (line 969) | last(): Some<T> {
method get (line 989) | get(idx: number): Option<T> {
method find (line 1008) | find(predicate:(v:T)=>boolean): Option<T> {
method contains (line 1024) | contains(v:T&WithEquality): boolean {
method take (line 1032) | take(n: number): LinkedList<T> {
method takeWhile (line 1047) | takeWhile(predicate: (x:T)=>boolean): LinkedList<T> {
method takeRightWhile (line 1065) | takeRightWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method drop (line 1075) | drop(n:number): LinkedList<T> {
method dropWhile (line 1089) | dropWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method dropRight (line 1103) | dropRight(n:number): LinkedList<T> {
method dropRightWhile (line 1114) | dropRightWhile(predicate:(x:T)=>boolean): LinkedList<T> {
method fold (line 1128) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 1146) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method foldRight (line 1170) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method zip (line 1188) | zip<U>(other: Iterable<U>): LinkedList<[T,U]> {
method zipWithIndex (line 1212) | zipWithIndex(): LinkedList<[T,number]> {
method reverse (line 1222) | reverse(): LinkedList<T> {
method span (line 1235) | span(predicate:(x:T)=>boolean): [LinkedList<T>,LinkedList<T>] {
method splitAt (line 1251) | splitAt(index:number): [LinkedList<T>,LinkedList<T>] {
method partition (line 1274) | partition(predicate:(v:T)=>boolean): [LinkedList<T>,LinkedList<T>] {
method groupBy (line 1297) | groupBy<C>(classifier: (v:T)=>C & WithEquality): HashMap<C,LinkedList<...
method arrangeBy (line 1314) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method shuffle (line 1321) | shuffle(): LinkedList<T> {
method append (line 1330) | append(v:T): LinkedList<T> {
method appendAll (line 1339) | appendAll(elts:Iterable<T>): LinkedList<T> {
method removeAll (line 1349) | removeAll(elts:Iterable<T&WithEquality>): LinkedList<T> {
method removeFirst (line 1357) | removeFirst(predicate: (x:T)=>boolean): LinkedList<T> {
method prepend (line 1375) | prepend(elt: T): LinkedList<T> {
method prependAll (line 1382) | prependAll(elts: Iterable<T>): LinkedList<T> {
method map (line 1396) | map<U>(mapper:(v:T)=>U): LinkedList<U> {
method mapOption (line 1416) | mapOption<U>(mapper:(v:T)=>Option<U>): LinkedList<U> {
method flatMap (line 1435) | flatMap<U>(mapper:(v:T)=>LinkedList<U>): LinkedList<U> {
method allMatch (line 1451) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 1459) | anyMatch(predicate:(v:T)=>boolean): boolean {
method filter (line 1470) | filter(predicate:(v:T)=>boolean): LinkedList<T> {
method sortBy (line 1493) | sortBy(compare: (v1:T,v2:T)=>Ordering): LinkedList<T> {
method sortOn (line 1513) | sortOn(...getKeys: Array<ToOrderable<T>|{desc:ToOrderable<T>}>): Linke...
method distinctBy (line 1524) | distinctBy<U>(keyExtractor: (x:T)=>U&WithEquality): LinkedList<T> {
method forEach (line 1531) | forEach(fn: (v:T)=>void): LinkedList<T> {
method reduce (line 1546) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 1556) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 1570) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 1583) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 1594) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 1606) | sumOn(getNumber: (v:T)=>number): number {
method sliding (line 1617) | sliding(count:number): Stream<ConsLinkedList<T>> {
method scanLeft (line 1629) | scanLeft<U>(init:U, fn:(soFar:U,cur:T)=>U): LinkedList<U> {
method scanRight (line 1650) | scanRight<U>(init:U, fn:(cur:T,soFar:U)=>U): LinkedList<U> {
method mkString (line 1669) | mkString(separator: string): string {
method toArray (line 1688) | toArray(): T[] {
method toVector (line 1702) | toVector(): Vector<T> {
method toMap (line 1716) | toMap<K,V>(converter:(x:T)=>[K & WithEquality,V]): HashMap<K,V> {
method toSet (line 1731) | toSet<K>(converter:(x:T)=>K&WithEquality): HashSet<K> {
method transform (line 1741) | transform<U>(converter:(x:LinkedList<T>)=>U): U {
method equals (line 1750) | equals(other: LinkedList<T&WithEquality>): boolean {
method hashCode (line 1792) | hashCode(): number {
method [inspect] (line 1802) | [inspect](): string {
method toString (line 1811) | toString(): string {
method [Symbol.iterator] (line 910) | [Symbol.iterator](): Iterator<T> {
FILE: src/Option.ts
type Option (line 43) | type Option<T> = Some<T> | None<T>;
class OptionStatic (line 48) | class OptionStatic {
method of (line 67) | of<T>(v: T|undefined): Option<T> {
method ofNullable (line 88) | ofNullable<T>(v:T|undefined|null): Option<T> {
method some (line 108) | some<T>(v: T): Some<T> {
method none (line 124) | none<T>(): Option<T> {
method isSome (line 137) | isSome<T>(o: Option<T>): o is Some<T> {
method isNone (line 149) | isNone<T>(o: Option<T>): o is None<T> {
method sequence (line 167) | sequence<T>(elts:Iterable<Option<T>>): Option<Vector<T>> {
method traverse (line 190) | traverse<T,U>(elts:Iterable<T>, fn: (x:T)=>Option<U>): Option<Vector<U...
method liftA2 (line 224) | liftA2<T,U,V>(fn:(v1:T,v2:U)=>V): (p1:Option<T>, p2:Option<U>) => Opti...
method liftAp (line 248) | liftAp<A,B>(fn:(x:A)=>B): (x: {[K in keyof A]: Option<A[K]>;}) => Opti...
method lift (line 282) | lift<T extends any[],U>(fn: (...args: T)=>U|undefined): (...args:T)=>O...
method liftNullable (line 313) | liftNullable<T extends any[],U>(fn: (...args: T)=>U|null|undefined): (...
method try_ (line 343) | try_<T>(fn:()=>T|undefined): Option<T> {
method tryNullable (line 367) | tryNullable<T>(fn:()=>T|null|undefined): Option<T> {
function optionHasTrueEquality (line 377) | function optionHasTrueEquality<T>(opt: Option<T>): boolean {
class Some (line 393) | class Some<T> implements Value {
method constructor (line 397) | constructor(private value: T) {}
method isSome (line 407) | isSome(): this is Some<T> {
method isNone (line 414) | isNone(): this is None<T> {
method asOption (line 422) | asOption(): Option<T> {
method get (line 431) | get(): T {
method orElse (line 439) | orElse(other: Option<T>): Option<T> {
method getOrThrow (line 449) | getOrThrow(errorInfo?: Error|string): T {
method contains (line 457) | contains(v: T&WithEquality): boolean {
method getOrUndefined (line 471) | getOrUndefined(): T | undefined {
method getOrNull (line 485) | getOrNull(): T | null {
method getOrElse (line 493) | getOrElse(alt: T): T {
method getOrCall (line 507) | getOrCall(fn: ()=>T): T {
method map (line 523) | map<U>(fn: (v:T)=>U): Option<U> {
method mapNullable (line 541) | mapNullable<U>(fn: (v:T)=>U|null|undefined): Option<U> {
method flatMap (line 551) | flatMap<U>(mapper:(v:T)=>Option<U>): Option<U> {
method filter (line 562) | filter(fn: (v:T)=>boolean): Option<T> {
method ifSome (line 570) | ifSome(fn:(v:T)=>void): Option<T> {
method ifNone (line 579) | ifNone(fn:()=>void): Option<T> {
method match (line 594) | match<U>(cases: {Some: (v:T)=>U, None: ()=>U}): U {
method transform (line 602) | transform<U>(converter:(x:Option<T>)=>U): U {
method toVector (line 611) | toVector(): Vector<T> {
method toEither (line 619) | toEither<L>(left: L): Either<L,T> {
method orCall (line 627) | orCall(_: () => Option<T>): Option<T> {
method hasTrueEquality (line 631) | hasTrueEquality<T>(): boolean {
method equals (line 640) | equals(other: Option<T&WithEquality>): boolean {
method hashCode (line 660) | hashCode(): number {
method toString (line 667) | toString(): string {
method [inspect] (line 674) | [inspect](): string {
class None (line 687) | class None<T> implements Value {
method isSome (line 697) | isSome(): this is Some<T> {
method isNone (line 704) | isNone(): this is None<T> {
method asOption (line 712) | asOption(): Option<T> {
method orElse (line 720) | orElse(other: Option<T>): Option<T> {
method getOrThrow (line 730) | getOrThrow(errorInfo?: Error|string): T & WithEquality {
method contains (line 741) | contains(v: T&WithEquality): boolean {
method getOrUndefined (line 755) | getOrUndefined(): T|undefined {
method getOrNull (line 769) | getOrNull(): T|null {
method getOrElse (line 777) | getOrElse(alt: T): T {
method getOrCall (line 791) | getOrCall(fn: ()=>T): T {
method map (line 807) | map<U>(fn: (v:T)=>U): Option<U> {
method mapNullable (line 825) | mapNullable<U>(fn: (v:T)=>U|null|undefined): Option<U> {
method flatMap (line 835) | flatMap<U>(mapper:(v:T)=>Option<U>): Option<U> {
method filter (line 846) | filter(fn: (v:T)=>boolean): Option<T> {
method ifSome (line 854) | ifSome(fn:(v:T)=>void): Option<T> {
method ifNone (line 862) | ifNone(fn:()=>void): Option<T> {
method match (line 878) | match<U>(cases: {Some: (v:T)=>U, None: ()=>U}): U {
method transform (line 886) | transform<U>(converter:(x:Option<T>)=>U): U {
method toVector (line 895) | toVector(): Vector<T> {
method toEither (line 903) | toEither<L>(left: L): Either<L,T> {
method orCall (line 911) | orCall(fn: () => Option<T>): Option<T> {
method hasTrueEquality (line 915) | hasTrueEquality<T>(): boolean {
method equals (line 924) | equals(other: Option<T&WithEquality>): boolean {
method hashCode (line 933) | hashCode(): number {
method toString (line 940) | toString(): string {
method [inspect] (line 947) | [inspect](): string {
FILE: src/Predicate.ts
type Predicate (line 37) | interface Predicate<T> {
class PredicateStatic (line 74) | class PredicateStatic {
method of (line 80) | of<T>(fn: (x:T)=>boolean): Predicate<T> {
method equals (line 92) | equals<T>(other: T&WithEquality): Predicate<T&WithEquality> {
method isIn (line 100) | isIn<T>(others: Iterable<T&WithEquality>): Predicate<T&WithEquality> {
method allOf (line 107) | allOf<T>(...predicates: Array<(x:T)=>boolean>): Predicate<T> {
method anyOf (line 114) | anyOf<T>(...predicates: Array<(x:T)=>boolean>): Predicate<T> {
method noneOf (line 121) | noneOf<T>(...predicates: Array<(x:T)=>boolean>): Predicate<T> {
FILE: src/Seq.ts
type IterableArray (line 15) | type IterableArray<T> = { [K in keyof T] : Iterable<T[K]> };
type Seq (line 21) | interface Seq<T> extends Collection<T> {
FILE: src/SeqHelpers.ts
function shuffle (line 14) | function shuffle(array: any[]) {
function arrangeBy (line 37) | function arrangeBy<T,K>(collection: Collection<T>, getKey: (v:T)=>K&With...
function seqHasTrueEquality (line 46) | function seqHasTrueEquality<T>(seq: Seq<T>): boolean {
function zipWithIndex (line 53) | function zipWithIndex<T>(seq: Seq<T>): Seq<[T,number]> {
function sortOn (line 60) | function sortOn<T>(seq: Seq<T>, getKeys: Array<ToOrderable<T>|{desc:ToOr...
function distinctBy (line 86) | function distinctBy<T,U>(seq: Collection<T>, keyExtractor: (x:T)=>U&With...
function toStringHelper (line 103) | function toStringHelper(
function minBy (line 140) | function minBy<T>(coll: Collection<T>, compare: (v1:T,v2:T)=>Ordering): ...
function minOn (line 147) | function minOn<T>(coll: Collection<T>, getSortable: ToOrderable<T>): Opt...
function maxBy (line 168) | function maxBy<T>(coll: Collection<T>, compare: (v1:T,v2:T)=>Ordering): ...
function maxOn (line 175) | function maxOn<T>(coll: Collection<T>, getSortable: ToOrderable<T>): Opt...
function sumOn (line 196) | function sumOn<T>(coll: Collection<T>, getNumber: (v:T)=>number): number {
function reduce (line 203) | function reduce<T>(coll: Collection<T>, combine: (v1:T,v2:T)=>T): Option...
function sliding (line 219) | function sliding<T>(seq: Seq<T>, count:number): Stream<Seq<T>> {
function removeAll (line 232) | function removeAll<T>(seq: Seq<T>, elts:Iterable<T&WithEquality>): Seq<T> {
FILE: src/Stream.ts
type Stream (line 39) | type Stream<T> = EmptyStream<T> | ConsStream<T>;
class StreamStatic (line 44) | class StreamStatic {
method empty (line 48) | empty<T>(): Stream<T> {
method of (line 57) | of<T>(...elts:T[]): Stream<T> {
method ofIterable (line 66) | ofIterable<T>(elts: Iterable<T>): Stream<T> {
method isEmpty (line 87) | isEmpty<T>(s: Stream<T>): s is EmptyStream<T> {
method isNotEmpty (line 100) | isNotEmpty<T>(s: Stream<T>): s is ConsStream<T> {
method ofArray (line 107) | private ofArray<T>(elts: Array<T>): Stream<T> {
method iterate (line 121) | iterate<T>(seed:T, fn: (v:T)=>T): ConsStream<T> {
method continually (line 134) | continually<T>(fn: ()=>T): ConsStream<T> {
method unfoldRight (line 152) | unfoldRight<T,U>(seed: T, fn: (x:T)=>Option<[U,T]>): Stream<U> {
method zip (line 178) | zip<A extends any[]>(...iterables: IterableArray<A>): Stream<A> {
class EmptyStream (line 205) | class EmptyStream<T> implements Seq<T> {
method asStream (line 230) | asStream(): Stream<T> {
method hasTrueEquality (line 237) | hasTrueEquality(): boolean {
method length (line 244) | length(): number {
method single (line 252) | single(): Option<T> {
method isEmpty (line 259) | isEmpty(): this is EmptyStream<T> {
method head (line 268) | head(): Option<T> {
method tail (line 276) | tail(): Option<Stream<T>> {
method last (line 285) | last(): Option<T> {
method get (line 298) | get(idx: number): Option<T> {
method find (line 307) | find(predicate:(v:T)=>boolean): Option<T> {
method contains (line 315) | contains(v:T&WithEquality): boolean {
method take (line 323) | take(n: number): Stream<T> {
method takeWhile (line 331) | takeWhile(predicate: (x:T)=>boolean): Stream<T> {
method takeRightWhile (line 343) | takeRightWhile(predicate:(x:T)=>boolean): Stream<T> {
method drop (line 353) | drop(n:number): Stream<T> {
method dropWhile (line 362) | dropWhile(predicate:(x:T)=>boolean): Stream<T> {
method dropRight (line 372) | dropRight(n:number): Stream<T> {
method dropRightWhile (line 381) | dropRightWhile(predicate:(x:T)=>boolean): Stream<T> {
method fold (line 395) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 413) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method foldRight (line 431) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method zip (line 449) | zip<U>(other: Iterable<U>): Stream<[T,U]> {
method zipWithIndex (line 461) | zipWithIndex(): Stream<[T,number]> {
method reverse (line 471) | reverse(): Stream<T> {
method span (line 484) | span(predicate:(x:T)=>boolean): [Stream<T>,Stream<T>] {
method splitAt (line 494) | splitAt(index:number): [Stream<T>,Stream<T>] {
method partition (line 510) | partition<U extends T>(predicate:(v:T)=>boolean): [Stream<U>,Stream<an...
method groupBy (line 522) | groupBy<C>(classifier: (v:T)=>C & WithEquality): HashMap<C,Stream<T>> {
method arrangeBy (line 532) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method shuffle (line 539) | shuffle(): Stream<T> {
method append (line 546) | append(v:T): Stream<T> {
method appendAll (line 553) | appendAll(elts:Iterable<T>): Stream<T> {
method removeAll (line 563) | removeAll(elts:Iterable<T&WithEquality>): Stream<T> {
method removeFirst (line 571) | removeFirst(predicate: (x:T)=>boolean): Stream<T> {
method appendStream (line 584) | appendStream(elts:Stream<T>): Stream<T> {
method prepend (line 591) | prepend(elt: T): Stream<T> {
method prependAll (line 598) | prependAll(elt: Iterable<T>): Stream<T> {
method cycle (line 609) | cycle(): Stream<T> {
method map (line 617) | map<U>(mapper:(v:T)=>U): Stream<U> {
method mapOption (line 631) | mapOption<U>(mapper:(v:T)=>Option<U>): Stream<U> {
method flatMap (line 641) | flatMap<U>(mapper:(v:T)=>Stream<U>): Stream<U> {
method allMatch (line 651) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 659) | anyMatch(predicate:(v:T)=>boolean): boolean {
method filter (line 670) | filter(predicate:(v:T)=>boolean): Stream<T> {
method sortBy (line 685) | sortBy(compare: (v1:T,v2:T)=>Ordering): Stream<T> {
method sortOn (line 705) | sortOn(...getKeys: Array<ToOrderable<T>|{desc:ToOrderable<T>}>): Strea...
method distinctBy (line 716) | distinctBy<U>(keyExtractor: (x:T)=>U&WithEquality): Stream<T> {
method forEach (line 723) | forEach(fn: (v:T)=>void): Stream<T> {
method reduce (line 733) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 743) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 757) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 767) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 781) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 793) | sumOn(getNumber: (v:T)=>number): number {
method sliding (line 804) | sliding(count:number): Stream<Stream<T>> {
method scanLeft (line 816) | scanLeft<U>(init:U, fn:(soFar:U,cur:T)=>U): Stream<U> {
method scanRight (line 829) | scanRight<U>(init:U, fn:(cur:T,soFar:U)=>U): Stream<U> {
method mkString (line 840) | mkString(separator: string): string {
method toArray (line 848) | toArray(): T[] {
method toVector (line 856) | toVector(): Vector<T> {
method toMap (line 870) | toMap<K,V>(converter:(x:T)=>[K & WithEquality,V]): HashMap<K,V> {
method toSet (line 882) | toSet<K>(converter:(x:T)=>K&WithEquality): HashSet<K> {
method toLinkedList (line 889) | toLinkedList(): LinkedList<T> {
method transform (line 897) | transform<U>(converter:(x:Stream<T>)=>U): U {
method equals (line 906) | equals(other: Stream<T&WithEquality>): boolean {
method hashCode (line 918) | hashCode(): number {
method [inspect] (line 922) | [inspect](): string {
method toString (line 931) | toString(): string {
method [Symbol.iterator] (line 215) | [Symbol.iterator](): Iterator<T> {
class ConsStream (line 944) | class ConsStream<T> implements Seq<T> {
method constructor (line 954) | public constructor(protected value: T, protected _tail: Lazy<Stream<T>...
method asStream (line 977) | asStream(): Stream<T> {
method hasTrueEquality (line 984) | hasTrueEquality(): boolean {
method length (line 991) | length(): number {
method single (line 999) | single(): Option<T> {
method isEmpty (line 1008) | isEmpty(): this is EmptyStream<T> {
method head (line 1017) | head(): Some<T> {
method tail (line 1025) | tail(): Some<Stream<T>> {
method last (line 1034) | last(): Some<T> {
method get (line 1054) | get(idx: number): Option<T> {
method find (line 1073) | find(predicate:(v:T)=>boolean): Option<T> {
method contains (line 1089) | contains(v:T&WithEquality): boolean {
method take (line 1097) | take(n: number): Stream<T> {
method takeWhile (line 1109) | takeWhile(predicate: (x:T)=>boolean): Stream<T> {
method takeRightWhile (line 1125) | takeRightWhile(predicate:(x:T)=>boolean): Stream<T> {
method drop (line 1135) | drop(n:number): Stream<T> {
method dropWhile (line 1149) | dropWhile(predicate:(x:T)=>boolean): Stream<T> {
method dropRight (line 1163) | dropRight(n:number): Stream<T> {
method dropRightWhile (line 1174) | dropRightWhile(predicate:(x:T)=>boolean): Stream<T> {
method fold (line 1188) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 1206) | foldLeft<U>(zero: U, fn:(soFar:U,cur:T)=>U): U {
method foldRight (line 1230) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method zip (line 1248) | zip<U>(other: Iterable<U>): Stream<[T,U]> {
method zipWithIndex (line 1269) | zipWithIndex(): Stream<[T,number]> {
method reverse (line 1279) | reverse(): Stream<T> {
method span (line 1292) | span(predicate:(x:T)=>boolean): [Stream<T>,Stream<T>] {
method splitAt (line 1302) | splitAt(index:number): [Stream<T>,Stream<T>] {
method partition (line 1318) | partition(predicate:(v:T)=>boolean): [Stream<T>,Stream<T>] {
method groupBy (line 1331) | groupBy<C>(classifier: (v:T)=>C & WithEquality): HashMap<C,Stream<T>> {
method arrangeBy (line 1346) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method shuffle (line 1353) | shuffle(): Stream<T> {
method append (line 1360) | append(v:T): Stream<T> {
method appendAll (line 1370) | appendAll(elts:Iterable<T>): Stream<T> {
method removeAll (line 1380) | removeAll(elts:Iterable<T&WithEquality>): Stream<T> {
method removeFirst (line 1388) | removeFirst(predicate: (x:T)=>boolean): Stream<T> {
method appendStream (line 1405) | appendStream(elts:Stream<T>): Stream<T> {
method prepend (line 1415) | prepend(elt: T): Stream<T> {
method prependAll (line 1424) | prependAll(elts: Iterable<T>): Stream<T> {
method cycle (line 1435) | cycle(): Stream<T> {
method _cycle (line 1439) | private _cycle(toRepeat: Stream<T>): Stream<T> {
method map (line 1450) | map<U>(mapper:(v:T)=>U): Stream<U> {
method mapOption (line 1465) | mapOption<U>(mapper:(v:T)=>Option<U>): Stream<U> {
method flatMap (line 1479) | flatMap<U>(mapper:(v:T)=>Stream<U>): Stream<U> {
method allMatch (line 1490) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 1498) | anyMatch(predicate:(v:T)=>boolean): boolean {
method filter (line 1509) | filter(predicate:(v:T)=>boolean): Stream<T> {
method sortBy (line 1527) | sortBy(compare: (v1:T,v2:T)=>Ordering): Stream<T> {
method sortOn (line 1547) | sortOn(...getKeys: Array<ToOrderable<T>|{desc:ToOrderable<T>}>): Strea...
method distinctBy (line 1558) | distinctBy<U>(keyExtractor: (x:T)=>U&WithEquality): Stream<T> {
method forEach (line 1565) | forEach(fn: (v:T)=>void): Stream<T> {
method reduce (line 1580) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 1590) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 1604) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 1614) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 1628) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 1640) | sumOn(getNumber: (v:T)=>number): number {
method sliding (line 1651) | sliding(count:number): Stream<Stream<T>> {
method scanLeft (line 1663) | scanLeft<U>(init:U, fn:(soFar:U,cur:T)=>U): Stream<U> {
method scanRight (line 1678) | scanRight<U>(init:U, fn:(cur:T,soFar:U)=>U): Stream<U> {
method mkString (line 1691) | mkString(separator: string): string {
method toArray (line 1710) | toArray(): T[] {
method toVector (line 1724) | toVector(): Vector<T> {
method toMap (line 1738) | toMap<K,V>(converter:(x:T)=>[K & WithEquality,V]): HashMap<K,V> {
method toSet (line 1753) | toSet<K>(converter:(x:T)=>K&WithEquality): HashSet<K> {
method toLinkedList (line 1762) | toLinkedList(): LinkedList<T> {
method transform (line 1770) | transform<U>(converter:(x:Stream<T>)=>U): U {
method equals (line 1779) | equals(other: Stream<T&WithEquality>): boolean {
method hashCode (line 1821) | hashCode(): number {
method [inspect] (line 1831) | [inspect](): string {
method toString (line 1840) | toString(): string {
method [Symbol.iterator] (line 959) | [Symbol.iterator](): Iterator<T> {
FILE: src/Tuple2.ts
class Tuple2 (line 16) | class Tuple2<T,U> implements Value {
method constructor (line 18) | private constructor(private _fst: T,
method of (line 24) | static of<T,U>(fst: T, snd: U) {
method ofArray (line 33) | static ofArray<T,U>(pair: Array<T|U>): Option<Tuple2<T,U>> {
method ofPair (line 44) | static ofPair<T,U>(pair: [T, U]): Tuple2<T,U> {
method hasTrueEquality (line 51) | hasTrueEquality(): boolean {
method fst (line 59) | fst(): T {
method snd (line 66) | snd(): U {
method map1 (line 73) | map1<V>(fn: (v:T)=>V): Tuple2<V,U> {
method map2 (line 80) | map2<V>(fn: (v:U)=>V): Tuple2<T,V> {
method map (line 87) | map<T1,U1>(fn: (a:T,b:U)=> Tuple2<T1,U1>): Tuple2<T1,U1> {
method transform (line 95) | transform<V>(converter:(x:Tuple2<T,U>)=>V): V {
method equals (line 104) | equals(other: Tuple2<T&WithEquality,U&WithEquality>): boolean {
method hashCode (line 121) | hashCode(): number {
method toPair (line 131) | toPair(): [T,U] {
method toArray (line 141) | toArray(): Array<T|U> {
method toVector (line 148) | toVector(): Vector<T|U> {
method toLinkedList (line 155) | toLinkedList(): ConsLinkedList<T|U> {
method toString (line 162) | toString(): string {
method [inspect] (line 170) | [inspect](): string {
FILE: src/Value.ts
type Value (line 10) | interface Value {
FILE: src/Vector.ts
class Vector (line 18) | class Vector<T> implements Seq<T> {
method constructor (line 24) | protected constructor(private _list: L.List<T>) {}
method empty (line 30) | static empty<T>(): Vector<T> {
method of (line 38) | static of<T>(...data: T[]): Vector<T> {
method ofIterable (line 47) | static ofIterable<T>(elts: Iterable<T>): Vector<T> {
method isEmpty (line 58) | static isEmpty<T>(v: Vector<T>): boolean {
method isNotEmpty (line 69) | static isNotEmpty<T>(v: Vector<T>): boolean {
method length (line 76) | length(): number {
method isEmpty (line 83) | isEmpty(): boolean {
method unfoldRight (line 101) | static unfoldRight<T,U>(seed: T, fn: (x:T)=>Option<[U,T]>): Vector<U> {
method get (line 116) | get(index: number): Option<T> {
method single (line 124) | single(): Option<T> {
method replace (line 134) | replace(index: number, val: T): Vector<T> {
method replaceFirst (line 149) | replaceFirst(element: T&WithEquality, newVal: T&WithEquality): Vector<...
method replaceAll (line 169) | replaceAll(element: T&WithEquality, newVal: T&WithEquality): Vector<T> {
method append (line 189) | append(val:T): Vector<T> {
method appendAll (line 197) | appendAll(elts: Iterable<T>): Vector<T> {
method removeAll (line 211) | removeAll(elts:Iterable<T&WithEquality>): Vector<T> {
method head (line 220) | head(): Option<T> {
method last (line 229) | last(): Option<T> {
method init (line 241) | init(): Vector<T> {
method dropWhile (line 250) | dropWhile(predicate:(x:T)=>boolean): Vector<T> {
method find (line 259) | find(predicate:(v:T)=>boolean): Option<T> {
method findLast (line 268) | findLast(predicate:(v:T)=>boolean): Option<T> {
method findIndex (line 277) | findIndex(predicate:(v:T)=>boolean): Option<number> {
method allMatch (line 287) | allMatch(predicate:(v:T)=>boolean): boolean {
method anyMatch (line 295) | anyMatch(predicate:(v:T)=>boolean): boolean {
method partition (line 311) | partition(predicate:(v:T)=>boolean): [Vector<T>,Vector<T>] {
method contains (line 320) | contains(v:T&WithEquality): boolean {
method groupBy (line 332) | groupBy<C>(classifier: (v:T)=>C & WithEquality): HashMap<C,Vector<T>> {
method arrangeBy (line 347) | arrangeBy<K>(getKey: (v:T)=>K&WithEquality): Option<HashMap<K,T>> {
method distinctBy (line 358) | distinctBy<U>(keyExtractor: (x:T)=>U&WithEquality): Vector<T> {
method forEach (line 369) | forEach(fun:(x:T)=>void):Vector<T> {
method map (line 378) | map<U>(fun:(x:T)=>U): Vector<U> {
method filter (line 389) | filter(fun:(v:T)=>boolean): Vector<T> {
method mapOption (line 403) | mapOption<U>(mapper:(v:T)=>Option<U>): Vector<U> {
method flatMap (line 420) | flatMap<U>(mapper:(v:T)=>Vector<U>): Vector<U> {
method fold (line 434) | fold(zero:T, fn:(v1:T,v2:T)=>T): T {
method foldLeft (line 452) | foldLeft<U>(zero:U, fn:(soFar:U,cur:T)=>U):U {
method foldRight (line 470) | foldRight<U>(zero: U, fn:(cur:T, soFar:U)=>U): U {
method indexOf (line 481) | indexOf(element: T & WithEquality): Option<number> {
method shuffle (line 489) | shuffle(): Vector<T> {
method transform (line 497) | transform<U>(converter:(x:Vector<T>)=>U): U {
method equals (line 506) | equals(other:Vector<T&WithEquality>): boolean {
method hashCode (line 537) | hashCode(): number {
method toString (line 550) | toString(): string {
method [inspect] (line 565) | [inspect](): string {
method mkString (line 576) | mkString(separator: string): string {
method sortBy (line 593) | sortBy(compare: (v1:T,v2:T)=>Ordering): Vector<T> {
method sortOn (line 613) | sortOn(...getKeys: Array<ToOrderable<T>|{desc:ToOrderable<T>}>): Vecto...
method toMap (line 627) | toMap<K,V>(converter:(x:T)=>[K & WithEquality,V]): HashMap<K,V> {
method toSet (line 642) | toSet<K>(converter:(x:T)=>K&WithEquality): HashSet<K> {
method toArray (line 651) | toArray(): T[] {
method hasTrueEquality (line 658) | hasTrueEquality(): boolean {
method zip (line 678) | static zip<A extends any[]>(...iterables: IterableArray<A>): Vector<A> {
method zip (line 704) | zip<U>(other: Iterable<U>): Vector<[T,U]> {
method reverse (line 725) | reverse(): Vector<T> {
method zipWithIndex (line 737) | zipWithIndex(): Vector<[T,number]> {
method takeWhile (line 745) | takeWhile(predicate:(x:T)=>boolean): Vector<T> {
method takeRightWhile (line 757) | takeRightWhile(predicate:(x:T)=>boolean): Vector<T> {
method splitAt (line 767) | splitAt(index:number): [Vector<T>,Vector<T>] {
method span (line 783) | span(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>] {
method drop (line 795) | drop(n:number): Vector<T> {
method take (line 806) | take(n:number): Vector<T> {
method prepend (line 816) | prepend(elt: T): Vector<T> {
method prependAll (line 823) | prependAll(elts: Iterable<T>): Vector<T> {
method removeFirst (line 831) | removeFirst(predicate: (v:T)=>boolean): Vector<T> {
method dropRight (line 842) | dropRight(n:number): Vector<T> {
method dropRightWhile (line 854) | dropRightWhile(predicate:(x:T)=>boolean): Vector<T> {
method tail (line 868) | tail(): Option<Vector<T>> {
method reduce (line 881) | reduce(combine: (v1:T,v2:T)=>T): Option<T> {
method minBy (line 891) | minBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method minOn (line 905) | minOn(getOrderable: ToOrderable<T>): Option<T> {
method maxBy (line 915) | maxBy(compare: (v1:T,v2:T)=>Ordering): Option<T> {
method maxOn (line 929) | maxOn(getOrderable: ToOrderable<T>): Option<T> {
method sumOn (line 941) | sumOn(getNumber: (v:T)=>number): number {
method sliding (line 952) | sliding(count:number): Stream<Vector<T>> {
method scanLeft (line 965) | scanLeft<U>(init:U, fn:(soFar:U,cur:T)=>U): Vector<U> {
method scanRight (line 978) | scanRight<U>(init:U, fn:(cur:T,soFar:U)=>U): Vector<U> {
method [Symbol.iterator] (line 362) | [Symbol.iterator](): Iterator<T> {
FILE: tests/Collection.ts
function runTests (line 10) | function runTests(seqName: string,
FILE: tests/Comments.ts
function getCodeSamples (line 18) | function getCodeSamples(commentText: string): Vector<string> {
type SampleInfo (line 50) | type SampleInfo = {
function getCommentsInlineCode (line 56) | function getCommentsInlineCode(scanner: ts.Scanner): Vector<SampleInfo> {
function getCodeSampleInfo (line 77) | function getCodeSampleInfo(identifier: string, codeSample: string): Samp...
function storeInVariable (line 92) | function storeInVariable(code: string) {
function generateTestContents (line 124) | function generateTestContents(fname: string, sampleInfo: SampleInfo) {
function generateTestFileContents (line 153) | function generateTestFileContents(fname: string, samplesInfo: Vector<Sam...
function generateTestCommentsFile (line 228) | function generateTestCommentsFile(filepath: string, fname: string): void {
function generateTestComments (line 245) | function generateTestComments(): void {
FILE: tests/DocLinks.ts
function checkUrlsInText (line 7) | function checkUrlsInText(text:string, urlFilter: (url:string)=>boolean):...
FILE: tests/Future.ts
function ensureFailedWithValue (line 8) | async function ensureFailedWithValue<T>(val: any, promise:Promise<T>) {
type Operation (line 189) | type Operation = {type:string, input: number, time: number};
FILE: tests/HashMap.ts
type MyEnum (line 13) | type MyEnum = "a" | "b" | "c";
class TestClass (line 168) | class TestClass implements HasEquals {
method constructor (line 169) | constructor(
method equals (line 173) | public equals(other: unknown): boolean {
method hashCode (line 177) | public hashCode(): number {
method equals (line 201) | equals(this:any, b:any) { return this.field1===b.field1 && this.field2==...
method hashCode (line 202) | hashCode(this:any) { return fieldsHashCode(this.field1, this.field2);}
FILE: tests/SampleData.ts
class MyClass (line 6) | class MyClass {
method constructor (line 7) | constructor(private field1:string, private field2:number) {}
method equals (line 8) | equals(other: MyClass): boolean {
method getField1 (line 15) | getField1(): string {
method getField2 (line 18) | getField2(): number {
method hashCode (line 21) | hashCode(): number {
method toString (line 24) | toString(): string {
class MySubclass (line 32) | class MySubclass extends MyClass {
method constructor (line 33) | constructor(field1: string, field2:number, private field3:string) {
method getField3 (line 37) | getField3(): string {
FILE: tests/Seq.ts
function runTests (line 20) | function runTests(seqName: string,
FILE: tests/TestHelpers.ts
constant TMP_FILENAME (line 5) | const TMP_FILENAME = "tmp.ts";
function diagnosticMsgToString (line 7) | function diagnosticMsgToString(diagMsg: string|ts.DiagnosticMessageChain...
function diagnosticMsgContains (line 21) | function diagnosticMsgContains(diagMsg: string|ts.DiagnosticMessageChain...
function assertFailCompile (line 28) | function assertFailCompile(contents: string, expectedMsg: string): void {
FILE: tests/Vector.ts
function checkTake (line 132) | function checkTake<T>(longer: Vector<T>, n: number, shorter: Vector<T>) {
function checkAppend (line 160) | function checkAppend<T>(base: Vector<T>, toAppend: Iterable<T>, combined...
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (562K chars).
[
{
"path": ".circleci/config.yml",
"chars": 1146,
"preview": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more "
},
{
"path": ".gitignore",
"chars": 67,
"preview": "dist/\nnode_modules/\napidoc/\nnpm-debug.log\ntests/apidoc-*\nyarn.lock\n"
},
{
"path": ".npmignore",
"chars": 236,
"preview": "# Required so that built files are included when using this github repo directly as an npm dependency\n#\n# See https://st"
},
{
"path": "LICENSE.TXT",
"chars": 730,
"preview": "Copyright 2017 Emmanuel Touzery\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with o"
},
{
"path": "README.md",
"chars": 9249,
"preview": "# prelude-ts\n\n[![NPM version][npm-image]][npm-url]\n[![Tests][circleci-image]][circleci-url]\n[![apidoc][apidoc-image]][ap"
},
{
"path": "benchmarks/bench.ts",
"chars": 14438,
"preview": "const Benchmark: any = require('benchmark');\n\nimport { execSync } from \"child_process\";\nimport { Vector } from \"../src/V"
},
{
"path": "package.json",
"chars": 1878,
"preview": "{\n \"name\": \"prelude-ts\",\n \"version\": \"1.0.6\",\n \"description\": \"A typescript functional programming library\",\n "
},
{
"path": "package.json.txt",
"chars": 735,
"preview": "Comments for the package.json cause I can't put them in the file itself...\n\n \"@types/node\": \"9.6.7\"\n => pin the type"
},
{
"path": "scripts/make_doc.sh",
"chars": 4158,
"preview": "#!/usr/bin/env bash\nset -e\n\nnpm install --no-package-lock\ntsc -p tsconfig.docgen.json\n\n# https://github.com/TypeStrong/t"
},
{
"path": "scripts/make_doc_extra/classes.ts",
"chars": 3063,
"preview": "import { readdirSync, writeFileSync } from \"fs\";\nimport { Vector } from \"../../src/Vector\";\nimport { Function4 } from \"."
},
{
"path": "scripts/make_doc_extra/globals.ts",
"chars": 3672,
"preview": "import { Vector } from \"../../src/Vector\";\nimport { HashMap } from \"../../src/HashMap\";\nimport { HashSet } from \"../../s"
},
{
"path": "scripts/make_doc_extra/helpers.ts",
"chars": 2024,
"preview": "import { Stream } from \"../../src/Stream\";\nimport { Vector } from \"../../src/Vector\";\nimport { readFileSync } from \"fs\";"
},
{
"path": "scripts/make_doc_extra/make_doc_extra.ts",
"chars": 281,
"preview": "import { groupGlobalsByCategory } from \"./globals\";\nimport { putClassStaticMethodsOnTop } from \"./classes\";\n\nconsole.log"
},
{
"path": "scripts/make_doc_extra/make_doc_preprocess.ts",
"chars": 1208,
"preview": "import { readFileSync, writeFileSync } from \"fs\";\n\ndeclare global {\n interface String {\n startsWith(other:stri"
},
{
"path": "scripts/prepublish.sh",
"chars": 661,
"preview": "#!/usr/bin/env bash\nset -e\n\nnode_modules/typescript/bin/tsc -p tsconfig.prepublish.json\nnode ./node_modules/browserify/b"
},
{
"path": "scripts/with_header.sh",
"chars": 303,
"preview": "#!/usr/bin/env bash\nset -e\n\ncat <<END\n/**\n * prelude-ts v$(node -p 'require(\"./package.json\").version')\n * https://githu"
},
{
"path": "src/ChromeDevToolFormatters.ts",
"chars": 4378,
"preview": "// http://bit.ly/object-formatters\n\ninterface ElementHandler {\n isElement(object:any): boolean;\n getHeader(object"
},
{
"path": "src/Collection.ts",
"chars": 4379,
"preview": "import { WithEquality, Ordering, ToOrderable } from \"./Comparison\";\nimport { Value } from \"./Value\";\nimport { Option } f"
},
{
"path": "src/Comparison.ts",
"chars": 8580,
"preview": "import { Option } from \"./Option\";\n\n/**\n * Sorting function for type T: function\n * to convert this type to a type which"
},
{
"path": "src/Contract.ts",
"chars": 1790,
"preview": "import { hasTrueEquality } from \"./Comparison\";\nimport { toStringHelper } from \"./SeqHelpers\";\n\nlet preludeTsContractVio"
},
{
"path": "src/Either.ts",
"chars": 28403,
"preview": "/**\n * The [[Either]] type represents an alternative between two value types.\n * A \"left\" value which is also conceptual"
},
{
"path": "src/Foldable.ts",
"chars": 1611,
"preview": "import { Option } from \"./Option\";\n\nexport interface Foldable<T> {\n\n /**\n * Reduces the collection to a single va"
},
{
"path": "src/Function.ts",
"chars": 18458,
"preview": "/**\n * Rich functions with helpers such as [[Function1.andThen]],\n * [[Function2.apply1]] and so on.\n *\n * We support fu"
},
{
"path": "src/Future.ts",
"chars": 15355,
"preview": "import { Vector } from \"./Vector\";\nimport { Option } from \"./Option\";\nimport { Either } from \"./Either\";\nimport { HashMa"
},
{
"path": "src/HashMap.ts",
"chars": 26255,
"preview": "import { IMap } from \"./IMap\";\nimport { hasEquals, HasEquals, WithEquality,\n getHashCode, areEqual, fieldsHashCo"
},
{
"path": "src/HashSet.ts",
"chars": 26181,
"preview": "import { ISet,\n SortOnSpec, SortBySpec, isSortOnSpec } from \"./ISet\";\nimport { Vector } from \"./Vector\";\nimport "
},
{
"path": "src/IMap.ts",
"chars": 6230,
"preview": "import { WithEquality } from \"./Comparison\";\nimport { Option } from \"./Option\";\nimport { Value } from \"./Value\"\nimport {"
},
{
"path": "src/ISet.ts",
"chars": 5774,
"preview": "import { WithEquality, Ordering, ToOrderable } from \"./Comparison\";\nimport { Value} from \"./Value\";\nimport { Collection "
},
{
"path": "src/Lazy.ts",
"chars": 1875,
"preview": "import { inspect } from \"./Value\";\nimport { toStringHelper } from \"./SeqHelpers\";\n\n/**\n * Represent a lazily evaluated v"
},
{
"path": "src/LinkedList.ts",
"chars": 57659,
"preview": "/**\n * A sequence of values, organized in-memory as a strict linked list.\n * Each element has an head (value) and a tail"
},
{
"path": "src/Option.ts",
"chars": 27530,
"preview": "/**\n * The [[Option]] type expresses that a value may be present or not.\n * The code is organized through the class [[No"
},
{
"path": "src/Predicate.ts",
"chars": 4360,
"preview": "/**\n * A predicate is a function taking one parameter and returning a boolean.\n * In other words the predicate checks wh"
},
{
"path": "src/Seq.ts",
"chars": 10897,
"preview": "import { WithEquality, Ordering, ToOrderable } from \"./Comparison\";\nimport { HashMap } from \"./HashMap\";\nimport { HashSe"
},
{
"path": "src/SeqHelpers.ts",
"chars": 6816,
"preview": "import { Option } from \"./Option\";\nimport { WithEquality, hasTrueEquality,\n Ordering, ToOrderable } from \"./Comp"
},
{
"path": "src/Stream.ts",
"chars": 56672,
"preview": "/**\n * A lazy, potentially infinite, sequence of values.\n *\n * The code is organized through the class [[EmptyStream]] ("
},
{
"path": "src/Tuple2.ts",
"chars": 4820,
"preview": "import { Value, inspect } from \"./Value\";\nimport { Option } from \"./Option\";\nimport { Vector } from \"./Vector\";\nimport {"
},
{
"path": "src/Value.ts",
"chars": 961,
"preview": "import { WithEquality } from \"./Comparison\";\n\nimport * as util from 'util';\n\n/**\n * @hidden\n */\nexport const inspect = S"
},
{
"path": "src/Vector.ts",
"chars": 31387,
"preview": "import { inspect } from './Value';\nimport { Option } from \"./Option\";\nimport { HashMap } from \"./HashMap\";\nimport { Hash"
},
{
"path": "src/index.ts",
"chars": 689,
"preview": "// Not re-exporting the abstract types such as Seq, Collection and so on,\n// on purpose. Right now they are more an help"
},
{
"path": "tests/Collection.ts",
"chars": 2549,
"preview": "import { Collection } from \"../src/Collection\";\nimport { HashMap } from \"../src/HashMap\";\nimport { WithEquality } from \""
},
{
"path": "tests/Comments.ts",
"chars": 9562,
"preview": "import { readFileSync, writeFileSync, readdirSync } from \"fs\";\nimport { Vector } from \"../src/Vector\";\nimport { Option }"
},
{
"path": "tests/Comparison.ts",
"chars": 691,
"preview": "import { typeOf } from \"../src/Comparison\";\nimport { Vector } from \"../src/Vector\";\nimport * as assert from 'assert'\n\nde"
},
{
"path": "tests/DocLinks.ts",
"chars": 1329,
"preview": "import { readFileSync } from \"fs\";\nimport { Vector } from \"../src/Vector\";\nimport { Future } from \"../src/Future\";\nimpor"
},
{
"path": "tests/Either.ts",
"chars": 10162,
"preview": "import { Either } from \"../src/Either\";\nimport { Vector } from \"../src/Vector\";\nimport { Seq } from \"../src/Seq\";\nimport"
},
{
"path": "tests/Function.ts",
"chars": 2928,
"preview": "import { Function0, Function1, Function2, Function3, Function4, Function5 } from '../src/Function';\nimport { Option } fr"
},
{
"path": "tests/Future.ts",
"chars": 16049,
"preview": "import { Future } from \"../src/Future\";\nimport { Vector } from \"../src/Vector\";\nimport { Option } from \"../src/Option\";\n"
},
{
"path": "tests/HashMap.ts",
"chars": 20179,
"preview": "import { HashMap } from \"../src/HashMap\";\nimport { HashSet } from \"../src/HashSet\";\nimport { Vector } from \"../src/Vecto"
},
{
"path": "tests/HashSet.ts",
"chars": 12487,
"preview": "import { HashSet } from \"../src/HashSet\";\nimport { Vector } from \"../src/Vector\";\nimport { Stream } from \"../src/Stream\""
},
{
"path": "tests/Lazy.ts",
"chars": 1255,
"preview": "import { Lazy } from \"../src/Lazy\";\nimport * as assert from 'assert'\n\ndescribe(\"lazy basics\", () => {\n it(\"should be "
},
{
"path": "tests/LinkedList.ts",
"chars": 2415,
"preview": "import { LinkedList, ConsLinkedList } from \"../src/LinkedList\";\nimport { typeOf } from \"../src/Comparison\";\nimport { Has"
},
{
"path": "tests/Option.ts",
"chars": 11105,
"preview": "import { Option, Some, None } from \"../src/Option\";\nimport { Vector } from \"../src/Vector\";\nimport { instanceOf } from \""
},
{
"path": "tests/Predicate.ts",
"chars": 1304,
"preview": "import { Predicate } from '../src/Predicate';\nimport * as assert from 'assert'\n\ndescribe(\"predicate composition\", () => "
},
{
"path": "tests/SampleData.ts",
"chars": 945,
"preview": "import { areEqual, fieldsHashCode } from \"../src/Comparison\";\n\n/**\n * @hidden\n */\nexport class MyClass {\n constructor"
},
{
"path": "tests/Seq.ts",
"chars": 30027,
"preview": "import { Seq } from \"../src/Seq\";\nimport { LinkedList } from \"../src/LinkedList\";\nimport { Vector } from \"../src/Vector\""
},
{
"path": "tests/Stream.ts",
"chars": 3928,
"preview": "import { Stream } from \"../src/Stream\";\nimport { typeOf } from \"../src/Comparison\";\nimport { Vector } from \"../src/Vecto"
},
{
"path": "tests/TestHelpers.ts",
"chars": 2281,
"preview": "import * as ts from \"typescript\";\nconst fs = require('fs');\nimport * as assert from 'assert'\n\nconst TMP_FILENAME = \"tmp."
},
{
"path": "tests/Tuple2.ts",
"chars": 1803,
"preview": "import { Tuple2 } from \"../src/Tuple2\";\nimport { Option } from \"../src/Option\";\nimport * as assert from 'assert'\n\ndescri"
},
{
"path": "tests/Vector.ts",
"chars": 11339,
"preview": "import { Vector } from \"../src/Vector\";\nimport { Stream } from \"../src/Stream\";\nimport { MyClass } from \"./SampleData\";\n"
},
{
"path": "tsconfig.benchmarks.json",
"chars": 107,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\n \"src/**/*\",\n \"benchmarks/**/*\"\n ]\n}\n"
},
{
"path": "tsconfig.docgen.json",
"chars": 104,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\n \"src/**/*\",\n \"scripts/**/*\"\n ]\n}\n"
},
{
"path": "tsconfig.json",
"chars": 398,
"preview": "{\n \"compilerOptions\": {\n \"noImplicitAny\": true,\n \"strictNullChecks\": true,\n \"noImplicitReturns\":"
},
{
"path": "tsconfig.prepublish.json",
"chars": 80,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\n \"src/**/*\"\n ]\n}\n"
},
{
"path": "tsconfig.test.json",
"chars": 102,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\n \"src/**/*\",\n \"tests/**/*\"\n ]\n}\n"
},
{
"path": "www_demo/index.html",
"chars": 646,
"preview": "<html>\n <head>\n <script src=\"../dist/src/prelude_ts.min.js\"></script>\n <script src=\"../dist/src/chrome_"
}
]
About this extraction
This page contains the full source code of the emmanueltouzery/prelude.ts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (526.1 KB), approximately 141.1k tokens, and a symbol index with 894 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.