@3fv/prelude-ts 中文文档教程
prelude-ts
ALL 归功于 prelude-ts
的原始创建者
这只是一个发布版本,添加了一些内容
Intro
prelude-ts(以前称为 prelude.ts)是一个 TypeScript 库,旨在进行函数式编程 在 TypeScript 中可访问且富有成效的概念。 请注意,即使它是 它是用 TypeScript 编写的,可以从 JavaScript(包括 ES5)中完美使用!
它提供持久性 不可变集合(Vector、Set、Map、Stream),以及诸如 Option、 要么,谓词和未来。
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)
这些集合也是 JavaScript 可迭代对象,所以如果你有 ES6 运行时, 您可以对它们使用 for .. of
结构。 如果你不熟悉 不可变集合,list.append(newItem)
保持list
不变; <代码>追加() 返回一个新列表。 不变性有助于推理代码。
您可以查看用户指南, 并浏览 API 文档, 或我们的博客。 请注意,构造函数是私有的,您应该使用静态方法来构建 项——例如:Option.of
、Vector.of
、Vector.ofIterable
等。
HashSet
和 HashMap
是使用 HAMT 算法, 以及具体的 hamt_plus 库。 Vector
是通过一个 位图矢量树 以及具体的列表库,从 0.7.7 开始。 此外,该库以惯用的 JavaScript 风格编写,带有循环 而不是递归,所以性能应该不错 (在此处查看与 immutable.js 等进行比较的基准)。 list
和 hamt_plus
是 prelude-ts 仅有的两个依赖项。
Set, Map and equality
JavaScript 没有结构相等性,除了原始类型。 所以 1 === 1
是真的,但是 [1] === [1]
不是,{a:1} === {a:1}。 这给集合带来了问题,因为如果你有一个
Set
,你就不会 由于平等的这种有限定义,需要重复元素。
出于这个原因,prelude-ts 鼓励你为你的非原始类型定义 方法 equals(other: any): boolean
和 hashCode(): number
(相同 immutable.js 使用的方法。 通过这些方法,结构平等是可以实现的,而且确实 Vector.of(1,2,3).equals(Vector.of(1,2,3))
为 true
。 然而这只能 如果您放入集合中的值本身已正确定义相等性,则工作 (看看 prelude-ts 如何提供帮助)。 如果这些价值观不具有结构平等,那么我们也不会比 ===
行为。
prelude-ts 试图帮助程序员解决这个问题; 它试图鼓励 开发商做正确的事。 它会拒绝没有明显正确的类型 在 Sets 和 Maps 键中定义了相等性,所以 HashSet.of([1])
, 或 Vector.of([1]).equals(Vector.of([2]))
将无法编译。 对于这两种情况,您都会收到(更长版本的)此消息:
Type 'number[]' is not assignable to type 'HasEquals'.
Property 'equals' is missing in type 'number[]'.
请参阅 用户指南 更多细节。
Installation
TypeScript 必须了解 Iterable
,这是一个 ES6 特性(但存在于大多数浏览器中) 编译prelude-ts。 如果您使用 TypeScript 并以 ES5 为目标,则对 tsconfig.json 的最小更改 可以添加:(
"lib": ["DOM", "ES5", "ScriptHost", "es2015.iterable"]
与默认的 ES5 设置相比,它只添加“es2015.iterable”)
Using in Node.js
只需在 package.json
中添加依赖项并开始使用它(比如 从 "prelude-ts" 导入 { Vector };
,或者 const { Vector } = require("prelude-ts");
如果你使用 commonjs)。 一切都应该有效,包括类型检查(如果您使用 TypeScript)。 prelude-ts 还提供 在节点 REPL 中漂亮地打印。
Using in the browser
在你的 package.json
中添加依赖; 打字稿应该自动 注册类型定义。
npm 包包含文件 dist/src/prelude_ts.js
和 dist/src/prelude_ts.min.js
, 这是 UMD 包; 他们与其他模块系统一起工作并设置 prelude_ts
如果未找到模块系统,则作为全局窗口。 在您的 脚本标签中的 index.html:
<script src="node_modules/prelude-ts/dist/src/prelude_ts.min.js"></script>
在您的应用程序中导入 prelude-ts 应该没有问题,但是如果您使用 模块它变得有点复杂。 如果您使用它们,一种解决方案是创建 一个包含以下内容的 imports.d.ts
文件:
// https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-283007750
import * as _P from 'prelude-ts';
export as namespace prelude_ts;
export = _P;
,在模块之外,您可以:
import Vector = prelude_ts.Vector;
然后在应用程序的 .ts
文件中 名称空间。
最后,如果您还包括 dist/src/chrome_dev_tools_formatters.js
通过 script
标签,以及启用 Chrome 自定义格式化程序, 那么你可以得到 在 Chrome 调试器中很好地显示了 prelude-ts 值。
Wishlist/upcoming features
- CharSeq, a string wrapper?
- Non-empty vector? (already have non-empty linkedlist)
- 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
我认为这些概念在语言中无法以足够好的方式表达 比如打字稿。
Alternatives and Influences
- monet.js -- only has the
List
andOption
collections, implemented in functional-style ES5. The implementation, using recursion, means its list type is noticeably slower than prelude-ts's. - immutable.js -- doesn't have the
Option
concept; the types can be clunky. - sanctuary offers global functions like
S.filter(S.where(...))
while prelude-ts prefers a fluent-API style likelist.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 offers global functions like
R.filter(R.where(...))
while prelude-ts prefers a fluent-API style likelist.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 & partial application are opt-in. - lodash also has global functions, and many functions mutate the collections.
- vavr -- 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
从 0.8.2 开始,prelude 需要 TypeScript 3.1 或更新版本。
Commands
npm install
npm test
npm run-script docgen
npm run benchmarks
prelude-ts
ALL credit goes to the original creator of prelude-ts
this is simply a published version with several additions
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 immutable collections (Vector, Set, Map, Stream), and constructs such as Option, Either, Predicate and Future.
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, and browse the API documentation, or our blog. 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, and concretely the hamt_plus library. Vector
is implemented through a bit-mapped vector trie and concretely the list library, 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). 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). 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). 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 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:
"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:
<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:
// 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:
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, then you can get a nice display of prelude-ts values in the Chrome debugger.
Wishlist/upcoming features
- CharSeq, a string wrapper?
- Non-empty vector? (already have non-empty linkedlist)
- 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 -- only has the
List
andOption
collections, implemented in functional-style ES5. The implementation, using recursion, means its list type is noticeably slower than prelude-ts's. - immutable.js -- doesn't have the
Option
concept; the types can be clunky. - sanctuary offers global functions like
S.filter(S.where(...))
while prelude-ts prefers a fluent-API style likelist.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 offers global functions like
R.filter(R.where(...))
while prelude-ts prefers a fluent-API style likelist.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 & partial application are opt-in. - lodash also has global functions, and many functions mutate the collections.
- vavr -- 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