├── .eslintignore ├── .eslintrc ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .yaspellerrc ├── LICENSE ├── README.md ├── either ├── LICENSE ├── README.md ├── index.ts └── package.json ├── identity ├── LICENSE ├── README.md ├── index.ts └── package.json ├── interfaces ├── LICENSE ├── README.md ├── alternative.ts ├── applicative.ts ├── async-applicative.ts ├── async-chainable.d.ts ├── async-functor.d.ts ├── async-monad.d.ts ├── catamorphism.ts ├── class-implements.d.ts ├── container.d.ts ├── functor.d.ts ├── index.d.ts ├── monad.d.ts └── package.json ├── iterator ├── LICENSE ├── README.md ├── filter-operation.ts ├── index.ts ├── intermediate-operation.ts ├── map-operation.ts └── package.json ├── jest.config.js ├── logo.svg ├── maybe ├── LICENSE ├── README.md ├── index.ts └── package.json ├── package-lock.json ├── package.json ├── tests ├── either.test.ts ├── identity.test.ts ├── iterator.test.ts └── maybe.test.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | # don't ever lint node_modules 2 | node_modules 3 | **/build/** -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "rules": { 11 | "@typescript-eslint/no-explicit-any": "off", 12 | "@typescript-eslint/explicit-module-boundary-types": "off" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.log 3 | .DS_Store 4 | 5 | build 6 | node_modules 7 | npm-debug.log 8 | 9 | **/tsconfig.json 10 | 11 | .idea/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn test 5 | yarn lint-staged 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | tmp/ 3 | out-tsc/ 4 | node_modules/ 5 | .vscode/ 6 | .idea/ 7 | .vscode/ 8 | .prettierignore 9 | package-lock.json 10 | package.json 11 | yarn.lock 12 | **/build/** 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "printWidth": 120, 4 | "semi": true, 5 | "singleQuote": false, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "trailingComma": "none", 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /.yaspellerrc: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "en", 3 | "ignoreCapitalization": true, 4 | "dictionary": [ 5 | "js", 6 | "JS", 7 | "Artem", 8 | "Kobzar", 9 | "iterable", 10 | "npm", 11 | "AsyncApplicative", 12 | "AsyncChainable", 13 | "AsyncFunctor", 14 | "AsyncFunctors", 15 | "auditable", 16 | "AsyncMonad", 17 | "Async", 18 | "async", 19 | "fmap", 20 | "haskell", 21 | "wikibooks", 22 | "asynchronously", 23 | "morphisms", 24 | "MonadPlus", 25 | "monoidal", 26 | "Homomorphism", 27 | "composable", 28 | "updatable", 29 | "undefinable", 30 | "iterables" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads 2 | 3 |

4 | 5 | Mmmm, sweet 6 | 7 |

8 | 9 | Easy-to-use monads implementation with static types definition and separated packages. 10 | 11 | - No dependencies, one small file 12 | - Easily auditable TypeScript/JS code 13 | 14 | ## Libraries 15 | 16 | - [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 17 | - [Either](https://github.com/JSMonk/sweet-monads/tree/master/either) 18 | - [Iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator) 19 | - [Identity](https://github.com/JSMonk/sweet-monads/tree/master/identity) 20 | - [Interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces) 21 | 22 | ## License 23 | 24 | MIT (c) Artem Kobzar see LICENSE file. 25 | -------------------------------------------------------------------------------- /either/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /either/README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads/either 2 | 3 | [Either Monad](https://hackage.haskell.org/package/category-extras-0.52.0/docs/Control-Monad-Either.html), The Either monad represents values with two possibilities: a value of type Either a b is either Left a or Right b. 4 | 5 | ### This library belongs to _sweet-monads_ project 6 | 7 | > **sweet-monads** — easy-to-use monads implementation with static types definition and separated packages. 8 | 9 | - No dependencies, one small file 10 | - Easily auditable TypeScript/JS code 11 | - Check out all libraries: 12 | [either](https://github.com/JSMonk/sweet-monads/tree/master/either), 13 | [iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator), 14 | [interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces), 15 | [maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 16 | 17 | ## Usage 18 | 19 | > npm install @sweet-monads/either 20 | 21 | ```typescript 22 | import { Either, right } from "@sweet-monads/either"; 23 | 24 | class UserNotFoundError extends Error { 25 | name: "UserNotFoundError"; 26 | } 27 | type User = { email: string; password: string }; 28 | 29 | function getUser(id: number): Either { 30 | return right({ email: "test@gmail.com", password: "test" }); 31 | } 32 | 33 | // Either 34 | const user = getUser(1).map(({ email }) => email); 35 | ``` 36 | 37 | ## API 38 | 39 | - [`chain`](#chain) 40 | - [`merge`](#merge) 41 | - [`mergeInOne`](#mergeinone) 42 | - [`mergeInMany`](#mergeinmany) 43 | - [`left`](#left) 44 | - [`right`](#right) 45 | - [`from`](#from) 46 | - [`fromTry`](#fromtry) 47 | - [`fromPromise`](#frompromise) 48 | - [`isEither`](#iseither) 49 | - [`Either#isLeft`](#eitherisleft) 50 | - [`Either#isRight`](#eitherisright) 51 | - [`Either#or`](#eitheror) 52 | - [`Either#join`](#eitherjoin) 53 | - [`Either#map`](#eithermap) 54 | - [`Either#mapRight`](#eithermapright) 55 | - [`Either#mapLeft`](#eithermapleft) 56 | - [`Either#asyncMap`](#eitherasyncmap) 57 | - [`Either#apply`](#eitherapply) 58 | - [`Either#asyncApply`](#eitherasyncapply) 59 | - [`Either#chain`](#eitherchain) 60 | - [`Either#asyncChain`](#eitherasyncchain) 61 | - [`Either#fold`](#eitherfold) 62 | - [Helpers](#helpers) 63 | 64 | #### `chain` 65 | 66 | ```typescript 67 | function chain(fn: (v: R) => Promise>): (m: Either) => Promise>; 68 | ``` 69 | 70 | - `fn: (v: R) => Promise>` - function which should be applied asynchronously to `Either` value 71 | - Returns function with `Either` argument and promised `Either` with new error or mapped by `fn` value (could be used inside `Promise#then` function). 72 | 73 | Example: 74 | 75 | ```typescript 76 | const getValue = async () => right(1); 77 | 78 | // Either 79 | const result = await getValue() 80 | .then(Either.chain(async v => right(v * 2))) 81 | .then(Either.chain(async v => left(new TypeError("Unexpected")))); 82 | ``` 83 | 84 | #### `merge` 85 | 86 | Alias for [`mergeInOne`](#mergeinone) 87 | 88 | ```typescript 89 | function merge(values: [Either]): Either; 90 | function merge(values: [Either, Either]): Either; 91 | function merge( 92 | values: [Either, Either, Either] 93 | ): Either; 94 | // ... until 10 elements 95 | ``` 96 | 97 | - `values: Array>` - Array of Either values which will be merged into Either of Array 98 | - Returns `Either>` which will contain `Right>` if all of array elements was `Right` otherwise `Left`. 99 | 100 | Example: 101 | 102 | ```typescript 103 | const v1 = right(2); // Either.Right 104 | const v2 = right("test"); // Either.Right 105 | const v3 = left(new Error()); // Either.Left 106 | 107 | merge([v1, v2]); // Either.Right 108 | merge([v1, v2, v3]); // Either.Left 109 | ``` 110 | 111 | #### `mergeInOne` 112 | 113 | ```typescript 114 | function merge(values: [Either]): Either; 115 | function merge(values: [Either, Either]): Either; 116 | function merge( 117 | values: [Either, Either, Either] 118 | ): Either; 119 | // ... until 10 elements 120 | ``` 121 | 122 | - `values: Array>` - Array of Either values which will be merged into Either of Array 123 | - Returns `Either>` which will contain `Right>` if all of array elements was `Right` otherwise `Left`. 124 | 125 | Example: 126 | 127 | ```typescript 128 | const v1 = right(2); // Either.Right 129 | const v2 = right("test"); // Either.Right 130 | const v3 = left(new Error()); // Either.Left 131 | 132 | merge([v1, v2]); // Either.Right 133 | merge([v1, v2, v3]); // Either.Left 134 | ``` 135 | 136 | #### `mergeInMany` 137 | 138 | ```typescript 139 | function mergeInMany(values: [Either]): Either, [R1]>; 140 | function mergeInMany(values: [Either, Either]): Either, [R1, R2]>; 141 | function mergeInMany( 142 | values: [Either, Either, Either] 143 | ): Either, [R1, R2, R3]>; 144 | // ... until 10 elements 145 | ``` 146 | 147 | - `values: Array>` - Array of Either values which will be merged into Either of Array 148 | - Returns `Either, Array>` which will contain `Right>` if all of array elements was `Right` otherwise array of all caught `Left` values. 149 | 150 | Example: 151 | 152 | ```typescript 153 | const v1 = right(2); // Either.Right 154 | const v2 = right("test"); // Either.Right 155 | const v3 = left(new Error()); // Either.Left 156 | 157 | merge([v1, v2]); // Either, [number, string]>.Right 158 | merge([v1, v2, v3]); // Either, [number, string, boolean]>.Left 159 | ``` 160 | 161 | #### `left` 162 | 163 | ```typescript 164 | function left(value: L): Either; 165 | ``` 166 | 167 | - Returns `Either` with `Left` state which contain value with `L` type. 168 | Example: 169 | 170 | ```typescript 171 | const v1 = left(new Error()); // Either.Left 172 | const v2 = left(new Error()); // Either.Left 173 | ``` 174 | 175 | #### `right` 176 | 177 | ```typescript 178 | function right(value: R): Either; 179 | ``` 180 | 181 | - Returns `Either` with `Right` state which contain value with `R` type. 182 | Example: 183 | 184 | ```typescript 185 | const v1 = right(2); // Either.Right 186 | const v2 = right(2); // Either.Right 187 | ``` 188 | 189 | #### `from` 190 | 191 | The same as [`right`](#right) 192 | 193 | Return only `Right` typed value. 194 | 195 | ```typescript 196 | function from(value: R): Either; 197 | ``` 198 | 199 | - Returns `Either` with `Right` state which contain value with `R` type. 200 | Example: 201 | 202 | ```typescript 203 | from(2); // Either.Right 204 | ``` 205 | 206 | #### `fromTry` 207 | 208 | Returns `Right` with function result or `Left` if function execution throws an error. 209 | 210 | ```typescript 211 | function fromTry(fn: () => R): Either; 212 | ``` 213 | 214 | ```typescript 215 | fromTry(() => 2); // Either.Right 216 | fromTry(() => { 217 | throw new Error("test"); 218 | }); // Either.Left 219 | ``` 220 | 221 | #### `fromPromise` 222 | 223 | Returns `Right` with the promise value if the provided promise fulfilled or `Left` with the error value if the provided promise rejected. 224 | 225 | ```typescript 226 | function fromPromise(promise: Promise): Promise>; 227 | ``` 228 | 229 | ```typescript 230 | fromPromise(Promise.resolve(2)); // Either.Right 231 | fromPromise(Promise.reject(new Error("test"))); // Either.Left 232 | ``` 233 | 234 | #### `isEither` 235 | 236 | ```typescript 237 | function isEither(value: unknown | Either): value is Either; 238 | ``` 239 | 240 | - Returns `boolean` if given `value` is instance of Either constructor. 241 | Example: 242 | 243 | ```typescript 244 | const value: unknown = 2; 245 | if (isEither(value)) { 246 | // ... value is Either at this block 247 | } 248 | ``` 249 | 250 | #### `Either#isLeft` 251 | 252 | ```typescript 253 | function isLeft(): boolean; 254 | ``` 255 | 256 | - Returns `true` if state of `Either` is `Left` otherwise `false` 257 | Example: 258 | 259 | ```typescript 260 | const v1 = right(2); 261 | const v2 = left(2); 262 | 263 | v1.isLeft(); // false 264 | v2.isLeft(); // true 265 | ``` 266 | 267 | #### `Either#isRight` 268 | 269 | ```typescript 270 | function isRight(): boolean; 271 | ``` 272 | 273 | - Returns `true` if state of `Either` is `Right` otherwise `false` 274 | Example: 275 | 276 | ```typescript 277 | const v1 = right(2); 278 | const v2 = left(2); 279 | 280 | v1.isRight(); // true 281 | v2.isRight(); // false 282 | ``` 283 | 284 | #### `Either#or` 285 | 286 | ```typescript 287 | function or(x: Either): Either; 288 | ``` 289 | 290 | - Returns `Either`. If state of `this` is `Right` then `this` will be returned otherwise `x` argument will be returned 291 | Example: 292 | 293 | ```typescript 294 | const v1 = right(2); 295 | const v2 = left("Error 1"); 296 | const v3 = left("Error 2"); 297 | const v4 = right(3); 298 | 299 | v1.or(v2); // v1 will be returned 300 | v2.or(v1); // v1 will be returned 301 | v2.or(v3); // v3 will be returned 302 | v1.or(v4); // v1 will be returned 303 | 304 | v2.or(v3).or(v1); // v1 will be returned 305 | v2.or(v1).or(v3); // v1 will be returned 306 | v1.or(v2).or(v3); // v1 will be returned 307 | ``` 308 | 309 | #### `Either#join` 310 | 311 | ```typescript 312 | function join(this: Either>): Either; 313 | ``` 314 | 315 | - `this: Either>` - `Either` instance which contains other `Either` instance as `Right` value. 316 | - Returns unwrapped `Either` - if current `Either` has `Right` state and inner `Either` has `Right` state then returns inner `Either` `Right`, if inner `Either` has `Left` state then return inner `Either` `Left` otherwise outer `Either` `Left`. 317 | Example: 318 | 319 | ```typescript 320 | const v1 = right(right(2)); 321 | const v2 = right(left(new Error())); 322 | const v3 = left>(new TypeError()); 323 | 324 | v1.join(); // Either.Right with value 2 325 | v2.join(); // Either.Left with value new Error 326 | v3.join(); // Either.Left with value new TypeError 327 | ``` 328 | 329 | #### `Either#map` 330 | 331 | ```typescript 332 | function map(fn: (val: R) => NewR): Either; 333 | ``` 334 | 335 | - Returns mapped by `fn` function value wrapped by `Either` if `Either` is `Right` otherwise `Left` with `L` value 336 | Example: 337 | 338 | ```typescript 339 | const v1 = right(2); 340 | const v2 = left(new Error()); 341 | 342 | const newVal1 = v1.map(a => a.toString()); // Either.Right with value "2" 343 | const newVal2 = v2.map(a => a.toString()); // Either.Left with value new Error() 344 | ``` 345 | 346 | #### `Either#mapRight` 347 | 348 | ```typescript 349 | function mapRight(fn: (val: R) => NewR): Either; 350 | ``` 351 | 352 | The same as [`Either#map`](#eithermap) 353 | 354 | - Returns mapped by `fn` function value wrapped by `Either` if `Either` is `Right` otherwise `Left` with `L` value 355 | Example: 356 | 357 | ```typescript 358 | const v1 = right(2); 359 | const v2 = left(new Error()); 360 | 361 | const newVal1 = v1.map(a => a.toString()); // Either.Right with value "2" 362 | const newVal2 = v2.map(a => a.toString()); // Either.Left with value new Error() 363 | ``` 364 | 365 | #### `Either#mapLeft` 366 | 367 | ```typescript 368 | function mapLeft(fn: (val: L) => NewL): Either; 369 | ``` 370 | 371 | - Returns mapped by `fn` function value wrapped by `Either` if `Either` is `Left` otherwise `Right` with `R` value 372 | Example: 373 | 374 | ```typescript 375 | const v1 = right(2); 376 | const v2 = left(new Error()); 377 | 378 | const newVal1 = v1.mapLeft(a => a.toString()); // Either.Right with value 2 379 | const newVal2 = v2.mapLeft(a => a.toString()); // Either.Left with value "Error" 380 | ``` 381 | 382 | ##### `Either#asyncMap` 383 | 384 | ```typescript 385 | function asyncMap(fn: (val: R) => Promise): Promise>; 386 | ``` 387 | 388 | - Returns `Promise` with mapped by `fn` function value wrapped by `Either` if `Either` is `Right` otherwise `Left` with value `L` 389 | Example: 390 | 391 | ```typescript 392 | const v1 = right(2); 393 | const v2 = left(new Error()); 394 | 395 | // Promise.Right> with value "2" 396 | const newVal1 = v1.asyncMap(a => Promise.resolve(a.toString())); 397 | // Promise.Left> with value new Error() 398 | const newVal2 = v2.asyncMap(a => Promise.resolve(a.toString())); 399 | ``` 400 | 401 | ##### `Either#apply` 402 | 403 | ```typescript 404 | function apply(this: Either B>, arg: Either): Either; 405 | function apply(this: Either, fn: Either B>): Either; 406 | ``` 407 | 408 | - `this | fn` - function wrapped by Either, which should be applied to value `arg` 409 | - `arg | this` - value which should be applied to `fn` 410 | - Returns mapped by `fn` function value wrapped by `Either` if `Either` is `Right` otherwise `Left` with `L` value 411 | Example: 412 | 413 | ```typescript 414 | const v1 = right(2); 415 | const v2 = left(new Error()); 416 | const fn1 = right number>((a: number) => a * 2); 417 | const fn2 = left number>(new Error()); 418 | 419 | const newVal1 = fn1.apply(v1); // Either.Right with value 4 420 | const newVal2 = fn1.apply(v2); // Either.Left with value new Error() 421 | const newVal3 = fn2.apply(v1); // Either.Left with value new Error() 422 | const newVal4 = fn2.apply(v2); // Either.Left with value new Error() 423 | ``` 424 | 425 | ##### `Either#asyncApply` 426 | 427 | Async variant of [`Either#apply`](#eitherapply) 428 | 429 | ```typescript 430 | function asyncApply(this: Either Promise>, arg: Either | A>): Promise>; 431 | function asyncApply(this: Either | A>, fn: Either Promise>): Promise>; 432 | function asyncApply( 433 | this: Either | A> | Either Promise>, 434 | argOrFn: Either | A> | Either Promise> 435 | ): Promise>; 436 | ``` 437 | 438 | - `this | fn` - function wrapped by Either, which should be applied to value `arg` 439 | - `arg | this` - value which should be applied to `fn` 440 | - Returns `Promise` with mapped by `fn` function value wrapped by `Either` if `Either` is `Right` otherwise `Left` with `L` value 441 | Example: 442 | 443 | ```typescript 444 | const v1 = right(2); 445 | const v2 = left(new Error()); 446 | const fn1 = right Promise>((a: number) => Promise.resolve(a * 2)); 447 | const fn2 = left Promise>(new Error()); 448 | 449 | const newVal1 = fn1.apply(v1); // Promise.Right> with value 4 450 | const newVal2 = fn1.apply(v2); // Promise.Left> with value new Error() 451 | const newVal3 = fn2.apply(v1); // Promise.Left> with value new Error() 452 | const newVal4 = fn2.apply(v2); // Promise.Left> with value new Error() 453 | ``` 454 | 455 | #### `Either#chain` 456 | 457 | ```typescript 458 | function chain(fn: (val: R) => Either): Either; 459 | ``` 460 | 461 | - Returns mapped by `fn` function value wrapped by `Either` if `Either` is `Right` and returned by `fn` value is `Right` too otherwise `Left` 462 | Example: 463 | 464 | ```typescript 465 | const v1 = right(2); 466 | const v2 = left(new Error()); 467 | 468 | // Either.Right with value "2" 469 | const newVal1 = v1.chain(a => right(a.toString())); 470 | // Either.Left with value new TypeError() 471 | const newVal2 = v1.chain(a => left(new TypeError())); 472 | // Either.Left with value new Error() 473 | const newVal3 = v2.chain(a => right(a.toString())); 474 | // Either.Left with value new Error() 475 | const newVal4 = v2.chain(a => left(new TypeError())); 476 | ``` 477 | 478 | ##### `Either#asyncChain` 479 | 480 | ```typescript 481 | function chain(fn: (val: R) => Promise>): Promise>; 482 | ``` 483 | 484 | - Returns `Promise` with mapped by `fn` function value wrapped by `Either` if `Either` is `Right` and returned by `fn` value is `Right` too otherwise `Left` 485 | Example: 486 | 487 | ```typescript 488 | const v1 = right(2); 489 | const v2 = left(new Error()); 490 | 491 | // Promise.Right> with value "2" 492 | const newVal1 = v1.asyncChain(a => right(a.toString())); 493 | // Promise.Left> with value new TypeError() 494 | const newVal2 = v1.asyncChain(a => left(new TypeError())); 495 | // Promise.Left> with value new Error() 496 | const newVal3 = v2.asyncChain(a => right(a.toString())); 497 | // Promise.Left> with value new Error() 498 | const newVal4 = v2.chain(a => left(new TypeError())); 499 | ``` 500 | 501 | ##### `Either#fold` 502 | 503 | ```typescript 504 | function fold(mapLeft: (value: L) => C, mapRight: (value: R) => C): C; 505 | ``` 506 | 507 | - Returns values mapped by `mapRight` if `Either` is `Right`, otherwise value mapped by `mapLeft` 508 | Example: 509 | 510 | ```typescript 511 | const v1 = right(2); 512 | const v2 = left(new Error()); 513 | 514 | // 4 515 | const newVal1 = v1.fold(() => 'fail', value => value * 2); 516 | // "fail" 517 | const newVal2 = v2.fold(() => 'fail', value => value * 2); 518 | ``` 519 | 520 | #### Helpers 521 | 522 | ```typescript 523 | // Value from Either instance 524 | const { value } = right(2); // number | Error 525 | const { value } = right(2); // number 526 | const { value } = left(new Error()); // number | Error 527 | const { value } = left(new Error()); // Error 528 | ``` 529 | 530 | ```typescript 531 | right(2).unwrap(); // number 532 | left(new TypeError()).unwrap(); // throws error 533 | 534 | right(2).unwrap(); // number 535 | left(new TypeError()).unwrap(x => x); // throws TypeError provied in arguments 536 | 537 | left(2).unwrapOr(3) // returns 3 538 | rigth(2).unwrapOr(3) // returns 2 539 | 540 | left(2).unwrapOrElse(num => num * 2) // returns 4 541 | right(2).unwrapOrElse(num => num * 2) // returns 2 542 | ``` 543 | 544 | ## License 545 | 546 | MIT (c) Artem Kobzar see LICENSE file. 547 | -------------------------------------------------------------------------------- /either/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AsyncMonad, 3 | Alternative, 4 | AsyncChainable, 5 | ClassImplements, 6 | MonadConstructor, 7 | ApplicativeConstructor, 8 | Container, 9 | Catamorphism 10 | } from "@sweet-monads/interfaces"; 11 | 12 | const enum EitherType { 13 | Left = "Left", 14 | Right = "Right" 15 | } 16 | 17 | function isWrappedFunction( 18 | m: Either> | Either B> 19 | ): m is Either B> { 20 | return typeof m.value === "function"; 21 | } 22 | 23 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 24 | type StaticCheck = ClassImplements< 25 | typeof EitherConstructor, 26 | [MonadConstructor, ApplicativeConstructor, AsyncChainable>] 27 | >; 28 | class EitherConstructor 29 | implements AsyncMonad, Alternative, Container, Catamorphism<[L, R]> { 30 | static chain(f: (v: R) => Promise>): (m: Either) => Promise>; 31 | static chain(f: (v: R) => Promise>): (m: Either) => Promise>; 32 | static chain(f: (v: R) => Promise>): (m: Either) => Promise>; 33 | static chain(f: (v: R) => Promise>) { 34 | return (m: Either): Promise> => m.asyncChain(f); 35 | } 36 | 37 | static mergeInOne(values: [Either]): Either; 38 | static mergeInOne(values: [Either, Either]): Either; 39 | static mergeInOne( 40 | values: [Either, Either, Either] 41 | ): Either; 42 | static mergeInOne( 43 | values: [Either, Either, Either, Either] 44 | ): Either; 45 | static mergeInOne( 46 | values: [Either, Either, Either, Either, Either] 47 | ): Either; 48 | static mergeInOne( 49 | values: [Either, Either, Either, Either, Either, Either] 50 | ): Either; 51 | static mergeInOne( 52 | values: [ 53 | Either, 54 | Either, 55 | Either, 56 | Either, 57 | Either, 58 | Either, 59 | Either 60 | ] 61 | ): Either; 62 | static mergeInOne( 63 | values: [ 64 | Either, 65 | Either, 66 | Either, 67 | Either, 68 | Either, 69 | Either, 70 | Either, 71 | Either 72 | ] 73 | ): Either; 74 | static mergeInOne( 75 | values: [ 76 | Either, 77 | Either, 78 | Either, 79 | Either, 80 | Either, 81 | Either, 82 | Either, 83 | Either, 84 | Either 85 | ] 86 | ): Either; 87 | static mergeInOne( 88 | values: [ 89 | Either, 90 | Either, 91 | Either, 92 | Either, 93 | Either, 94 | Either, 95 | Either, 96 | Either, 97 | Either, 98 | Either 99 | ] 100 | ): Either; 101 | static mergeInOne(either: Array>): Either; 102 | static mergeInOne(eithers: Array>) { 103 | return eithers.reduce( 104 | (res: Either>, v) => res.chain(res => v.map(v => res.concat([v]))), 105 | EitherConstructor.right>([]) 106 | ); 107 | } 108 | 109 | static merge = EitherConstructor.mergeInOne; 110 | 111 | static mergeInMany(values: [Either]): Either, [R1]>; 112 | static mergeInMany(values: [Either, Either]): Either, [R1, R2]>; 113 | static mergeInMany( 114 | values: [Either, Either, Either] 115 | ): Either, [R1, R2, R3]>; 116 | static mergeInMany( 117 | values: [Either, Either, Either, Either] 118 | ): Either, [R1, R2, R3, R4]>; 119 | static mergeInMany( 120 | values: [Either, Either, Either, Either, Either] 121 | ): EitherConstructor, [R1, R2, R3, R4, R5]>; 122 | static mergeInMany( 123 | values: [Either, Either, Either, Either, Either, Either] 124 | ): Either, [R1, R2, R3, R4, R5, R6]>; 125 | static mergeInMany( 126 | values: [ 127 | Either, 128 | Either, 129 | Either, 130 | Either, 131 | Either, 132 | Either, 133 | Either 134 | ] 135 | ): Either, [R1, R2, R3, R4, R5, R6, R7]>; 136 | static mergeInMany( 137 | values: [ 138 | Either, 139 | Either, 140 | Either, 141 | Either, 142 | Either, 143 | Either, 144 | Either, 145 | Either 146 | ] 147 | ): EitherConstructor, [R1, R2, R3, R4, R5, R6, R7, R8]>; 148 | static mergeInMany( 149 | values: [ 150 | Either, 151 | Either, 152 | Either, 153 | Either, 154 | Either, 155 | Either, 156 | Either, 157 | Either, 158 | Either 159 | ] 160 | ): Either, [R1, R2, R3, R4, R5, R6, R7, R8, R9]>; 161 | static mergeInMany( 162 | values: [ 163 | Either, 164 | Either, 165 | Either, 166 | Either, 167 | Either, 168 | Either, 169 | Either, 170 | Either, 171 | Either, 172 | Either 173 | ] 174 | ): Either, [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10]>; 175 | static mergeInMany(either: Array>): Either; 176 | static mergeInMany(eithers: Array>): EitherConstructor { 177 | return eithers.reduce>( 178 | ( 179 | res: EitherConstructor, 180 | v 181 | ): EitherConstructor => { 182 | if (res.isLeft()) { 183 | return v.isLeft() ? EitherConstructor.left(res.value.concat([v.value])) : res; 184 | } 185 | return v.isLeft() 186 | ? EitherConstructor.left([v.value]) 187 | : (res.chain(res => v.map(v => [...res, v])) as EitherConstructor); 188 | }, 189 | EitherConstructor.right([]) 190 | ); 191 | } 192 | 193 | static from(v: T): Either { 194 | return EitherConstructor.right(v); 195 | } 196 | 197 | static fromPromise(promise: Promise): Promise> { 198 | return promise.then(EitherConstructor.right).catch(e => EitherConstructor.left(e as L)); 199 | } 200 | 201 | static fromTry(fn: () => R): Either { 202 | try { 203 | return EitherConstructor.right(fn()); 204 | } catch (e) { 205 | return EitherConstructor.left(e as L); 206 | } 207 | } 208 | 209 | static right(v: T): Either { 210 | return new EitherConstructor(EitherType.Right, v); 211 | } 212 | 213 | static left(v: T): Either { 214 | return new EitherConstructor(EitherType.Left, v); 215 | } 216 | 217 | private constructor(private readonly type: T, public readonly value: T extends EitherType.Left ? L : R) {} 218 | 219 | isLeft(): this is EitherConstructor { 220 | return this.type === EitherType.Left; 221 | } 222 | 223 | isRight(): this is EitherConstructor { 224 | return this.type === EitherType.Right; 225 | } 226 | 227 | join(this: Either>): Either { 228 | return this.chain(x => x); 229 | } 230 | 231 | mapRight(f: (r: R) => T): Either { 232 | return this.map(f); 233 | } 234 | 235 | mapLeft(f: (l: L) => T): Either { 236 | if (this.isLeft()) { 237 | return EitherConstructor.left(f(this.value as L)); 238 | } 239 | return EitherConstructor.right(this.value as R); 240 | } 241 | 242 | map(f: (r: R) => T): Either { 243 | if (this.isLeft()) { 244 | return EitherConstructor.left(this.value as L); 245 | } 246 | return EitherConstructor.right(f(this.value as R)); 247 | } 248 | 249 | asyncMap(f: (r: R) => Promise): Promise> { 250 | if (this.isLeft()) { 251 | return Promise.resolve(EitherConstructor.left(this.value as L)); 252 | } 253 | return f(this.value as R).then(v => EitherConstructor.right(v)); 254 | } 255 | 256 | apply(this: Either B>, arg: Either): Either; 257 | apply(this: Either, fn: Either B>): Either; 258 | apply( 259 | this: Either | Either B>, 260 | argOrFn: Either | Either B> 261 | ): EitherConstructor { 262 | if (this.isLeft()) { 263 | return EitherConstructor.left(this.value as L); 264 | } 265 | if (argOrFn.isLeft()) { 266 | return EitherConstructor.left(argOrFn.value as L); 267 | } 268 | if (isWrappedFunction(this)) { 269 | return (argOrFn as Either).map(this.value as (a: A) => B); 270 | } 271 | if (isWrappedFunction(argOrFn)) { 272 | return (argOrFn as Either B>).apply(this as Either); 273 | } 274 | throw new Error("Some of the arguments should be a function"); 275 | } 276 | 277 | asyncApply(this: Either Promise>, arg: Either | A>): Promise>; 278 | asyncApply(this: Either | A>, fn: Either B>>): Promise>; 279 | asyncApply( 280 | this: Either | A> | Either Promise>, 281 | argOrFn: Either | A> | Either Promise> 282 | ): Promise> { 283 | if (this.isLeft()) { 284 | return Promise.resolve(EitherConstructor.left(this.value as L)); 285 | } 286 | if (argOrFn.isLeft()) { 287 | return Promise.resolve(EitherConstructor.left(argOrFn.value as L)); 288 | } 289 | if (isWrappedFunction(this)) { 290 | return (argOrFn as Either | A>) 291 | .map(a => Promise.resolve(a)) 292 | .asyncMap(pa => pa.then(this.value as (a: A) => Promise)); 293 | } 294 | if (isWrappedFunction(argOrFn)) { 295 | return (argOrFn as Either | A) => Promise>).asyncApply(this as Either>); 296 | } 297 | throw new Error("Some of the arguments should be a function"); 298 | } 299 | 300 | chain(f: (r: R) => Either): Either { 301 | if (this.isLeft()) { 302 | return EitherConstructor.left(this.value as L); 303 | } 304 | return f(this.value as R); 305 | } 306 | 307 | asyncChain(f: (r: R) => Promise>): Promise> { 308 | if (this.isLeft()) { 309 | return Promise.resolve(EitherConstructor.left(this.value)); 310 | } 311 | return f(this.value as R); 312 | } 313 | 314 | or(x: Either): Either { 315 | return this.isLeft() ? x : (this as Either); 316 | } 317 | 318 | unwrap(errorFactory: (x: L) => unknown = () => new Error("Either state is Left")): R { 319 | if (this.isRight()) return this.value; 320 | throw errorFactory(this.value as L); 321 | } 322 | 323 | unwrapOr(x: R): R { 324 | return this.isRight() ? this.value : x; 325 | } 326 | 327 | unwrapOrElse(f: (l: L) => R): R { 328 | return this.isRight() ? this.value : f(this.value as L); 329 | } 330 | 331 | fold(mapLeft: (l: L) => C, mapRight: (r: R) => C) { 332 | return this.isRight() ? mapRight(this.value as R) : mapLeft((this as Either).value as L); 333 | } 334 | 335 | get [Symbol.toStringTag]() { 336 | return "Either"; 337 | } 338 | } 339 | 340 | export type Either = EitherConstructor | EitherConstructor; 341 | 342 | export const { merge, mergeInOne, mergeInMany, left, right, from, fromTry, fromPromise, chain } = EitherConstructor; 343 | 344 | export const isEither = (value: unknown | Either): value is Either => 345 | value instanceof EitherConstructor; 346 | -------------------------------------------------------------------------------- /either/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sweet-monads/either", 3 | "version": "3.3.1", 4 | "description": "Either monad", 5 | "main": "./cjs/index.js", 6 | "module": "./esm/index.js", 7 | "exports": { 8 | "types": "./index.d.ts", 9 | "import": "./esm/index.js", 10 | "require": "./cjs/index.js" 11 | }, 12 | "scripts": { 13 | "build": "run-s build:pre build:all build:after", 14 | "build:clean": "rm -rf build && mkdir build", 15 | "build:config": "cp ../tsconfig.json tsconfig.json", 16 | "build:pre": "run-p build:clean build:config", 17 | "build:esm": "tsc --project ./tsconfig.json --module 'ESNext' --outDir './build/esm'", 18 | "build:cjs": "tsc --project ./tsconfig.json --module 'CommonJS' --outDir './build/cjs'", 19 | "build:declaration": "tsc --project ./tsconfig.json --outDir './build' --emitDeclarationOnly", 20 | "build:all": "run-p build:esm build:cjs build:declaration", 21 | "build:copy": "cp ./package.json ./build/package.json && cp ./README.md ./build/README.md", 22 | "build:fixcjs": "echo '{\"type\":\"commonjs\"}' > ./build/cjs/package.json", 23 | "build:fixesm": "echo '{\"type\":\"module\"}' > ./build/esm/package.json", 24 | "build:after": "run-p build:copy build:fixcjs build:fixesm" 25 | }, 26 | "homepage": "https://github.com/JSMonk/sweet-monads/tree/master/either", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/JSMonk/sweet-monads/tree/master/either" 30 | }, 31 | "dependencies": { 32 | "@sweet-monads/interfaces": "^3.3.0" 33 | }, 34 | "author": "JSMonk", 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /identity/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /identity/README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads/identity 2 | 3 | [Identity Monad](https://blog.ploeh.dk/2022/05/16/the-identity-monad/), The Identity monad is a monad that does not embody any computational strategy. It simply applies the bound function to its input without any modification. 4 | 5 | 6 | ### This library belongs to _sweet-monads_ project 7 | 8 | > **sweet-monads** — easy-to-use monads implementation with static types definition and separated packages. 9 | 10 | - No dependencies, one small file 11 | - Easily auditable TypeScript/JS code 12 | - Check out all libraries: 13 | [either](https://github.com/JSMonk/sweet-monads/tree/master/either), 14 | [iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator), 15 | [interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces), 16 | [maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 17 | [identity](https://github.com/JSMonk/sweet-monads/tree/master/identity) 18 | 19 | ## Usage 20 | 21 | > npm install @sweet-monads/identity 22 | 23 | ```typescript 24 | // Before 25 | app.use( 26 | express.static( 27 | path.resolve(getDirname(import.meta.url), "../public") 28 | ) 29 | ) 30 | 31 | 32 | // After 33 | Identity.from(import.meta.url) 34 | .map(getDirname) 35 | .map(dir => path.resolve(dir, "../public")) 36 | .map(express.static) 37 | .map(app.use); 38 | ``` 39 | 40 | ## API 41 | 42 | - [`chain`](#chain) 43 | - [`from`](#from) 44 | - [`isIdentity`](#isidentity) 45 | - [`Identity#join`](#identityjoin) 46 | - [`Identity#map`](#identitymap) 47 | - [`Identity#asyncMap`](#identityasyncmap) 48 | - [`Identity#apply`](#identityapply) 49 | - [`Identity#asyncApply`](#identityasyncapply) 50 | - [`Identity#chain`](#identitychain) 51 | - [`Identity#asyncChain`](#identityasyncchain) 52 | - [Helpers](#helpers) 53 | 54 | #### `chain` 55 | 56 | ```typescript 57 | function chain(fn: (v: A) => Promise>): (m: Identity) => Promise>; 58 | ``` 59 | 60 | - `fn: (v: A) => Promise>` - function which should be applied asynchronously to `Identity` value 61 | - Returns function with `Identity` argument and mapped by `fn` value (could be used inside `Promise#then` function). 62 | 63 | Example: 64 | 65 | ```typescript 66 | const getValue = async () => from(1); 67 | 68 | // Identity 69 | const result = await getValue() 70 | .then(Identity.chain(async v => from(v * 2))) 71 | .then(Identity.chain(async () => from(null))); 72 | ``` 73 | 74 | #### `from` 75 | 76 | ```typescript 77 | function from(value: T): Identity; 78 | ``` 79 | 80 | - Returns `Identity` which contains value with `T` type. 81 | Example: 82 | 83 | ```typescript 84 | const v1 = from(2); // Identity 85 | const v2 = from<2>(2); // Identity<2> 86 | ``` 87 | 88 | 89 | #### `isIdentity` 90 | 91 | ```typescript 92 | function isIdentity(value: unknown | Identity): value is Identity; 93 | ``` 94 | 95 | - Returns `boolean` if given `value` is instance of Identity constructor. 96 | Example: 97 | 98 | ```typescript 99 | const value: unknown = 2; 100 | if (isIdentity(value)) { 101 | // ... value is Identity at this block 102 | } 103 | ``` 104 | 105 | 106 | #### `Identity#join` 107 | 108 | ```typescript 109 | function join(this: Identity>): Identity; 110 | ``` 111 | 112 | - `this: Identity>` - `Identity` instance which contains another `Identity` instance. 113 | - Returns unwrapped (inner) `Identity`. 114 | Example: 115 | 116 | ```typescript 117 | const v = from(from(2)); 118 | 119 | v1.join(); // Identity with value 2 120 | ``` 121 | 122 | #### `Identity#map` 123 | 124 | ```typescript 125 | function map(fn: (val: Val) => NewVal): Identity; 126 | ``` 127 | 128 | - Returns mapped by `fn` function value wrapped with `Identity`. 129 | Example: 130 | 131 | ```typescript 132 | const v = just(2); 133 | 134 | const newVal = v.map(a => a.toString()); // Identity with value "2" 135 | ``` 136 | 137 | ##### `Identity#asyncMap` 138 | 139 | ```typescript 140 | function asyncMap(fn: (val: Val) => Promise): Promise>; 141 | ``` 142 | 143 | - Returns `Promise` with mapped by `fn` function value wrapped with `Identity` 144 | Example: 145 | 146 | ```typescript 147 | const v = from(2); 148 | 149 | // Promise> with value "2" 150 | const newVal = v.asyncMap(a => Promise.resolve(a.toString())); 151 | ``` 152 | 153 | ##### `Identity#apply` 154 | 155 | ```typescript 156 | function apply(this: Identity<(a: A) => B>, arg: Identity): Identity; 157 | function apply(this: Identity, fn: Identity<(a: A) => B>): Identity; 158 | ``` 159 | 160 | - `this | fn` - function wrapped by Identity, which should be applied to value `arg` 161 | - `arg | this` - value which should be applied to `fn` 162 | - Returns mapped by `fn` function value wrapped by `Identity`. 163 | Example: 164 | 165 | ```typescript 166 | const v = from(2); 167 | const fn = from((a: number) => a * 2); 168 | 169 | const newVal1 = fn.apply(v); // Identity with value 4 170 | const newVal2 = v.apply(fn); // Identity with value 4 171 | ``` 172 | 173 | ##### `Identity#asyncApply` 174 | 175 | Async variant of [`Identity#apply`](#identityapply) 176 | 177 | ```typescript 178 | function asyncApply( 179 | this: Identity<(a: Promise | A) => Promise>, 180 | arg: Identity | A> 181 | ): Promise>; 182 | function asyncApply(this: Identity | A>, fn: Identity<(a: Promise | A) => Promise>): Promise>; 183 | ``` 184 | 185 | - `this | fn` - function wrapped by Identity, which should be applied to value `arg` 186 | - `arg | this` - value which should be applied to `fn` 187 | - Returns `Promise` with mapped by `fn` function value wrapped by `Identity`. 188 | Example: 189 | 190 | ```typescript 191 | const v = from(2); 192 | const fn = from((a: number) => Promise, resolve(a * 2)); 193 | 194 | const newVal1 = fn.apply(v); // Promise> with value 4 195 | const newVal2 = v.apply(fn); // Promise> with value 4 196 | ``` 197 | 198 | #### `Identity#chain` 199 | 200 | ```typescript 201 | function chain(fn: (val: Val) => Identity): Identity; 202 | ``` 203 | 204 | - Returns mapped by `fn` function value wrapped by `Identity` 205 | Example: 206 | 207 | ```typescript 208 | const v = from(2); 209 | 210 | const newVal = v1.chain(a => from(a.toString())); // Identity with value "2" 211 | ``` 212 | 213 | ##### `Identity#asyncChain` 214 | 215 | ```typescript 216 | function asyncChain(fn: (val: Val) => Promise>): Promise>; 217 | ``` 218 | 219 | - Returns `Promise` with mapped by `fn` function value wrapped with `Identity`. 220 | Example: 221 | 222 | ```typescript 223 | const v = from(2); 224 | 225 | // Promise> with value "2" 226 | const newVal = v.asyncChain(a => Promise.resolve(from(a.toString()))); 227 | ``` 228 | 229 | #### Helpers 230 | 231 | ```typescript 232 | // Value from Identity instance 233 | const { value } = from(2); // number 234 | ``` 235 | 236 | ```typescript 237 | const value = from(2).unwrap(); // returns 2 238 | ``` 239 | 240 | ## License 241 | 242 | MIT (c) Artem Kobzar see LICENSE file. 243 | -------------------------------------------------------------------------------- /identity/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AsyncMonad, 3 | AsyncChainable, 4 | ClassImplements, 5 | MonadConstructor, 6 | ApplicativeConstructor, 7 | Container 8 | } from "@sweet-monads/interfaces"; 9 | 10 | function isWrappedFunction(m: Identity | Identity<(a: A) => B>): m is Identity<(a: A) => B> { 11 | return typeof m.value === "function"; 12 | } 13 | 14 | function isWrappedAsyncFunction( 15 | m: Identity> | Identity<(a: A) => B | Promise> 16 | ): m is Identity<(a: A) => B> { 17 | return typeof m.value === "function"; 18 | } 19 | 20 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 21 | type StaticCheck = ClassImplements< 22 | typeof Identity, 23 | [MonadConstructor, ApplicativeConstructor, AsyncChainable>] 24 | >; 25 | export default class Identity implements AsyncMonad, Container { 26 | static chain(f: (v: A) => Promise>) { 27 | return (m: Identity): Promise> => m.asyncChain(f); 28 | } 29 | 30 | static from(v: T): Identity { 31 | return new Identity(v); 32 | } 33 | 34 | private constructor(public readonly value: T) {} 35 | 36 | join(this: Identity>): Identity { 37 | return this.chain(x => x); 38 | } 39 | 40 | map(f: (r: T) => V): Identity { 41 | return Identity.from(f(this.value)); 42 | } 43 | 44 | asyncMap(f: (r: T) => Promise): Promise> { 45 | return f(this.value).then(Identity.from); 46 | } 47 | 48 | apply(this: Identity<(a: A) => B>, arg: Identity): Identity; 49 | apply(this: Identity, fn: Identity<(a: A) => B>): Identity; 50 | apply(this: Identity | Identity<(a: A) => B>, argOrFn: Identity | Identity<(a: A) => B>): Identity { 51 | if (isWrappedFunction(this)) { 52 | return (argOrFn as Identity).map(this.value as (a: A) => B); 53 | } 54 | if (isWrappedFunction(argOrFn)) { 55 | return (argOrFn as Identity<(a: A) => B>).apply(this as Identity); 56 | } 57 | throw new Error("Some of the arguments should be a function"); 58 | } 59 | 60 | asyncApply(this: Identity<(a: A) => Promise>, arg: Identity | A>): Promise>; 61 | asyncApply(this: Identity | A>, fn: Identity<(a: A) => Promise>): Promise>; 62 | asyncApply( 63 | this: Identity | A> | Identity<(a: A) => Promise>, 64 | argOrFn: Identity | A> | Identity<(a: A) => Promise> 65 | ): Promise> { 66 | if (isWrappedAsyncFunction(this)) { 67 | return (argOrFn as Identity | A>) 68 | .map(a => Promise.resolve(a)) 69 | .asyncMap(pa => pa.then(this.value as (a: A) => Promise)); 70 | } 71 | if (isWrappedAsyncFunction(argOrFn)) { 72 | return (argOrFn as Identity<(a: A) => Promise>).asyncApply(this as Identity>); 73 | } 74 | throw new Error("Some of the arguments should be a function"); 75 | } 76 | 77 | chain(f: (r: T) => Identity): Identity { 78 | return f(this.value as T); 79 | } 80 | 81 | asyncChain(f: (r: T) => Promise>): Promise> { 82 | return f(this.value as T); 83 | } 84 | 85 | unwrap(): T { 86 | return this.value; 87 | } 88 | 89 | get [Symbol.toStringTag]() { 90 | return this.constructor.name; 91 | } 92 | } 93 | 94 | export const { from, chain } = Identity; 95 | 96 | export const isIdentity = (value: unknown | Identity): value is Identity => value instanceof Identity; 97 | -------------------------------------------------------------------------------- /identity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sweet-monads/identity", 3 | "version": "3.3.1", 4 | "description": "", 5 | "main": "./cjs/index.js", 6 | "module": "./esm/index.js", 7 | "exports": { 8 | "types": "./index.d.ts", 9 | "import": "./esm/index.js", 10 | "require": "./cjs/index.js" 11 | }, 12 | "homepage": "https://github.com/JSMonk/sweet-monads/tree/master/identity", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/JSMonk/sweet-monads/tree/master/identity" 16 | }, 17 | "scripts": { 18 | "build": "run-s build:pre build:all build:after", 19 | "build:clean": "rm -rf build && mkdir build", 20 | "build:config": "cp ../tsconfig.json tsconfig.json", 21 | "build:pre": "run-p build:clean build:config", 22 | "build:esm": "tsc --project ./tsconfig.json --module 'ESNext' --outDir './build/esm'", 23 | "build:cjs": "tsc --project ./tsconfig.json --module 'CommonJS' --outDir './build/cjs'", 24 | "build:declaration": "tsc --project ./tsconfig.json --outDir './build' --emitDeclarationOnly", 25 | "build:all": "run-p build:esm build:cjs build:declaration", 26 | "build:copy": "cp ./package.json ./build/package.json && cp ./README.md ./build/README.md", 27 | "build:fixcjs": "echo '{\"type\":\"commonjs\"}' > ./build/cjs/package.json", 28 | "build:fixesm": "echo '{\"type\":\"module\"}' > ./build/esm/package.json", 29 | "build:after": "run-p build:copy build:fixcjs build:fixesm" 30 | }, 31 | "dependencies": { 32 | "@sweet-monads/interfaces": "^3.2.0" 33 | }, 34 | "author": "JSMonk", 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /interfaces/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /interfaces/README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads/interfaces 2 | 3 | Collection of interfaces which describe functional programming abstractions. 4 | 5 | ### This library belongs to _sweet-monads_ project 6 | 7 | > **sweet-monads** — easy-to-use monads implementation with static types definition and separated packages. 8 | 9 | - No dependencies, one small file 10 | - Easily auditable TypeScript/JS code 11 | - Check out all libraries: 12 | [either](https://github.com/JSMonk/sweet-monads/tree/master/either), 13 | [iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator), 14 | [interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces), 15 | [maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 16 | [identity](https://github.com/JSMonk/sweet-monads/tree/master/identity) 17 | 18 | ## Usage 19 | 20 | > npm install @sweet-monads/interfaces 21 | 22 | ```typescript 23 | import { Functor } from "@sweet-monads/interfaces"; 24 | 25 | class MyClass implements Functor { 26 | map(fn: (i: T) => A): MyClass { 27 | return new MyClass(); 28 | } 29 | } 30 | ``` 31 | 32 | ## Available Interfaces 33 | 34 | - [`Functor`](#functor) 35 | - [`AsyncFunctor`](#asyncfunctor) 36 | - [`Alternative`](#alternative) 37 | - [`Applicative`](#applicative) 38 | - [`AsyncApplicative`](#asyncapplicative) 39 | - [`Monad`](#monad) 40 | - [`AsyncMonad`](#asyncmonad) 41 | - [`AsyncChainable`](#asyncchainable) 42 | 43 | ### Functor 44 | 45 | https://wiki.haskell.org/Functor 46 | 47 | An abstract datatype `Functor`, which has the ability for it's value(s) to be mapped over can become an instance of the Functor interface. That is to say, a new Functor, `Functor` can be made from `Functor` by transforming all of it's value(s), whilst leaving the structure of f itself unmodified. 48 | 49 | Functors are required to obey certain laws in regards to their mapping. Ensuring instances of Functor obey these laws means the behaviour of fmap remains predictable. 50 | 51 | Methods: 52 | 53 | ##### `Functor#map` 54 | 55 | ```typescript 56 | function map(f: (x: A) => B): Functor; 57 | ``` 58 | 59 | #### Minimal Complete Definition 60 | 61 | ```typescript 62 | map(f: (x: A) => B): Functor; 63 | ``` 64 | 65 | #### Functor Laws 66 | 67 | ##### Functors must preserve identity morphisms 68 | 69 | ```typescript 70 | const f = new SomeFunctorImplementation(); // for all functors 71 | const id = x => x; 72 | 73 | expect(f.map(id)).toEqual(f); 74 | ``` 75 | 76 | ##### Functors preserve composition of morphisms 77 | 78 | ```typescript 79 | declare function twice(x: number): number; // for all functions 80 | declare function toString(x: number): string; // for all functions 81 | 82 | const f = new SomeFunctorImplementation(); 83 | 84 | expect(f.map(x => toString(twice(x)))).toEqual(f.map(twice).map(toString)); 85 | ``` 86 | 87 | ### AsyncFunctor 88 | 89 | Async version of [`Functor`](#functor), which provides async version of the [`map`](#functormap) method. 90 | 91 | Methods: 92 | 93 | ##### `AsyncFunctor#asyncMap` 94 | 95 | ```typescript 96 | function asyncMap(f: (x: A) => Promise): Promise>; 97 | ``` 98 | 99 | #### Minimal Complete Definition 100 | 101 | [`Functor`](#functor) implementation. 102 | 103 | ```typescript 104 | asyncMap(f: (x: A) => Promise): Promise>; 105 | ``` 106 | 107 | #### AsyncFunctor Laws 108 | All the [`Functor Laws`](#functor-laws) should be applied to the async version, so: 109 | 110 | ##### AsyncFunctors must preserve identity morphisms 111 | ```typescript 112 | const f = new SomeAsyncFunctorImplementation(); // for all functors 113 | const id = x => Promise.resolve(x); 114 | 115 | expect(await f.asyncMap(id)).toEqual(f); 116 | ``` 117 | 118 | ##### AsyncFunctors preserve composition of morphisms 119 | 120 | ```typescript 121 | declare function twice(x: number): Promise; // for all functions 122 | declare function toString(x: number): Promise; // for all functions 123 | Promise.resolve(toString(twice(x))) 124 | 125 | const f = new SomeAsyncFunctorImplementation(); 126 | expect(await f.asyncMap(x => twice(x).then(toString))).toEqual(await f.asyncMap(twice).then(f => f.asyncMap(toString))); 127 | ``` 128 | 129 | 130 | ### Alternative 131 | 132 | https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus 133 | 134 | Several classes (Applicative, Monad) have "monoidal" subclasses, intended to model computations that support "failure" and "choice" (in some appropriate sense). 135 | The basic intuition is that `empty` represents some sort of "failure", and `or` represents a choice between alternatives. (However, this intuition does not fully capture the nuance possible; see the section on Laws below.) Of course, `or` should be associative and `empty` should be the identity element for it. Instances of Alternative must implement `empty` and `or`; some and many have default implementations but are included in the class since specialized implementations may be more efficient than the default. 136 | 137 | Current implementation is not fully port of `Alternative` from Haskell, because we don't make the interface an child interface of [`Applicative`](#applicative) and dropped `empty` static member for ability to implement `Alternative` for classes like `Either`. 138 | 139 | Methods: 140 | 141 | ##### `Alternative#or` 142 | 143 | ```typescript 144 | function or(arg: Alternative): Alternative; 145 | ``` 146 | 147 | ### Applicative 148 | 149 | https://wiki.haskell.org/Applicative_functor 150 | 151 | This module describes a structure intermediate between a functor and a monad (technically, a strong lax monoidal functor). Compared with monads, this interface lacks the full power of the binding operation `chain`. 152 | 153 | Methods: 154 | 155 | ##### `Applicative.from` 156 | 157 | ```typescript 158 | function from(x: A): Applicative; 159 | ``` 160 | 161 | ##### `Applicative#apply` 162 | 163 | ```typescript 164 | function apply(this: Applicative<(a: A) => B>, arg: Applicative): Applicative; 165 | function apply(this: Applicative, fn: Applicative<(a: A) => B>): Applicative; 166 | ``` 167 | 168 | #### Minimal Complete Definition 169 | 170 | [`Functor`](#functor) implementation. 171 | 172 | ```typescript 173 | static from(x: A): Applicative; 174 | ``` 175 | 176 | ```typescript 177 | apply(this: Applicative<(a: A) => B>, arg: Applicative): Applicative; 178 | apply(this: Applicative, fn: Applicative<(a: A) => B>): Applicative; 179 | ``` 180 | 181 | #### Applicative Laws 182 | 183 | ##### Identity Law 184 | 185 | ```typescript 186 | declare var x: Applicative; 187 | const id = x => x; 188 | 189 | expect(SomeApplicative.from(id).apply(x)).toEqual(x); 190 | ``` 191 | 192 | ##### Homomorphism Law 193 | 194 | ```typescript 195 | declare var x: unknown; 196 | declare var f: (x: unknown) => unknown; 197 | 198 | expect(SomeApplicative.from(f).apply(x)).toEqual(SomeApplicative.from(f(x))); 199 | ``` 200 | 201 | ### AsyncApplicative 202 | 203 | Async version of [`Applicative`](#applicative), which provides async version of the [`apply`](#applicativeapply) method. 204 | 205 | Methods: 206 | 207 | ##### `AsyncApplicative#asyncApply` 208 | 209 | ```typescript 210 | function asyncApply(this: AsyncApplicative<(a: A) => Promise>, arg: AsyncApplicative | A>): Promise>; 211 | function asyncApply(this: AsyncApplicative | A>, fn: AsyncApplicative<(a: A) => Promise>): Promise>; 212 | ``` 213 | 214 | #### Minimal Complete Definition 215 | 216 | [`Applicative`](#applicative) implementation. 217 | 218 | [`AsyncFunctor`](#asyncfunctor) implementation. 219 | 220 | ```typescript 221 | asyncApply(this: AsyncApplicative<(a: A) => Promise>, arg: AsyncApplicative | A>): Promise>; 222 | asyncApply(this: AsyncApplicative | A>, fn: AsyncApplicative<(a: A) => Promise>): Promise>; 223 | ``` 224 | 225 | #### AsyncApplicative Laws 226 | 227 | All the [`Applicative Laws`](#applicative-laws) should be applied to the async version, so: 228 | 229 | ##### Identity Law 230 | 231 | ```typescript 232 | declare var x: AsyncApplicative; 233 | const id = x => Promise.resolve(x); 234 | 235 | expect(await SomeAsyncApplicative.from(id).asyncApply(x)).toEqual(x); 236 | ``` 237 | 238 | ##### Homomorphism Law 239 | 240 | ```typescript 241 | declare var x: unknown; 242 | declare var f: (x: unknown) => Promise; 243 | 244 | expect(await SomeAsyncApplicative.from(f).asyncApply(x)).toEqual(SomeAsyncApplicative.from(await f(x))); 245 | ``` 246 | 247 | ### Monad 248 | 249 | https://wiki.haskell.org/Monad 250 | 251 | Monads can be thought of as composable computation descriptions. The essence of monad is thus separation of composition timeline from the composed computation's execution timeline, as well as the ability of computation to implicitly carry extra data, as pertaining to the computation itself, in addition to its one (hence the name) output, that it will produce when run (or queried, or called upon). This lends monads to supplementing pure calculations with features like I/O, common environment, updatable state, etc. 252 | 253 | Methods: 254 | 255 | ##### `Monad#chain` 256 | 257 | ```typescript 258 | function chain(f: (x: A) => Monad): Monad; 259 | ``` 260 | 261 | ##### `Monad#join` 262 | 263 | ```typescript 264 | function join(this: Monad>): Monad; 265 | ``` 266 | 267 | #### Minimal Complete Definition 268 | 269 | [`Applicative`](#applicative) implementation. 270 | 271 | ```typescript 272 | chain(f: (x: A) => Monad): Monad; 273 | join(this: Monad>): Monad; 274 | ``` 275 | 276 | #### Monad Laws 277 | 278 | ##### Left identity Law 279 | 280 | ```typescript 281 | declare var x: unknown; 282 | declare function f(x: unknown): Monad; 283 | 284 | expect(SomeMonad.from(x).chain(f)).toEqual(f(x)); 285 | ``` 286 | 287 | ##### Right identity Law 288 | 289 | ```typescript 290 | declare var mx: Monad; 291 | declare function f(x: unknown): Monad; 292 | 293 | expect(mx.chain(SomeMonad.from)).toEqual(mx); 294 | ``` 295 | 296 | ##### Associativity Law 297 | 298 | ```typescript 299 | declare var mx: Monad; 300 | declare function f(x: unknown): Monad; 301 | declare function g(x: unknown): Monad; 302 | 303 | expect(mx.chain(x => f(x).chain(g))).toEqual(mx.chain(f).chain(g)); 304 | ``` 305 | 306 | ### AsyncMonad 307 | 308 | Async version of [`Monad`](#monad), which provides async version of the [`chain`](#applicativeapply) method. 309 | 310 | Methods: 311 | 312 | ##### `Monad#chain` 313 | 314 | ```typescript 315 | function asyncChain(f: (x: A) => Promise>): Promise>; 316 | ``` 317 | 318 | #### Minimal Complete Definition 319 | 320 | [`Monad`](#monad) implementation. 321 | 322 | [`AsyncApplicative`](#applicative) implementation. 323 | 324 | ```typescript 325 | asyncChain(f: (x: A) => Promise>): Promise>; 326 | ``` 327 | 328 | #### AsyncMonad Laws 329 | 330 | All the [`Monad Laws`](#monad-laws) should be applied to the async version, so: 331 | 332 | ##### Left identity Law 333 | 334 | ```typescript 335 | declare var x: unknown; 336 | declare function f(x: unknown): Promise>; 337 | 338 | expect(await SomeAsyncMonad.from(x).asyncChain(f)).toEqual(await f(x)); 339 | ``` 340 | 341 | ##### Right identity Law 342 | 343 | ```typescript 344 | declare var mx: AsyncMonad; 345 | declare function f(x: unknown): Promise>; 346 | 347 | expect(await mx.asyncChain(x => Promise.resolve(SomeAsyncMonad.from(x)))).toEqual(mx); 348 | ``` 349 | 350 | ##### Associativity Law 351 | 352 | ```typescript 353 | declare var ax: AsyncMonad; 354 | declare function f(x: unknown): Promise>; 355 | declare function g(x: unknown): Promise>; 356 | 357 | expect(await ax.asyncChain(x => f(x).then(fx => fx.asyncChain(g)))).toEqual(await ax.asyncChain(f).then(fx => fx.asyncChain(g))); 358 | ``` 359 | 360 | 361 | ### AsyncChainable 362 | 363 | Static interface which give an ability to use `AsyncMonad` more comfortable with `Promise`. 364 | 365 | > Should be used with `ClassImplements` decorator 366 | 367 | Methods: 368 | 369 | ##### `AsyncChainable#chain` 370 | 371 | ```typescript 372 | function chain(f: (v: A) => Promise>): (m: M & AsyncMonad) => Promise>; 373 | ``` 374 | 375 | #### Usage 376 | 377 | ```typescript 378 | @ClassImplements> 379 | class IdentityMonad extends AsyncMonad { /*...*/ } 380 | 381 | declare function getAsyncValue(): Promise> 382 | declare function sendToServer(value: number): Promise> 383 | 384 | const value = await getAsyncValue().then(chain(sendToServer)); 385 | ``` 386 | 387 | ### Container 388 | 389 | Is a value wrapper, that allows to get value (if state of the container is valid), or throws error if not. 390 | 391 | Methods: 392 | 393 | ##### `Container#unwrap` 394 | 395 | ```typescript 396 | const lucky = Math.random() > 0.5 ? just(":)") : none(); 397 | 398 | // Will either return ":)" or throw an error 399 | lucky.unwrap(); 400 | ``` 401 | 402 | ## License 403 | 404 | MIT (c) Artem Kobzar see LICENSE file. 405 | -------------------------------------------------------------------------------- /interfaces/alternative.ts: -------------------------------------------------------------------------------- 1 | export interface Alternative { 2 | or(arg: Alternative): Alternative; 3 | } 4 | -------------------------------------------------------------------------------- /interfaces/applicative.ts: -------------------------------------------------------------------------------- 1 | import type { Functor } from "./functor"; 2 | 3 | export interface Applicative extends Functor { 4 | apply(this: Applicative<(a: A) => B>, arg: Applicative): Applicative; 5 | apply(this: Applicative, fn: Applicative<(a: A) => B>): Applicative; 6 | } 7 | 8 | export interface ApplicativeConstructor { 9 | from(item: I): Applicative; 10 | } 11 | -------------------------------------------------------------------------------- /interfaces/async-applicative.ts: -------------------------------------------------------------------------------- 1 | import type { Applicative } from "./applicative"; 2 | import type { AsyncFunctor } from "./async-functor"; 3 | 4 | export interface AsyncApplicative extends AsyncFunctor, Applicative { 5 | asyncApply( 6 | this: AsyncApplicative<(a: A) => Promise>, 7 | arg: AsyncApplicative | A> 8 | ): Promise>; 9 | asyncApply( 10 | this: AsyncApplicative | A>, 11 | fn: AsyncApplicative<(a: A) => Promise> 12 | ): Promise>; 13 | } 14 | -------------------------------------------------------------------------------- /interfaces/async-chainable.d.ts: -------------------------------------------------------------------------------- 1 | import type { AsyncMonad } from "./async-monad"; 2 | 3 | export interface AsyncChainable> { 4 | chain(f: (v: A) => Promise>): (m: M & AsyncMonad) => Promise>; 5 | } 6 | -------------------------------------------------------------------------------- /interfaces/async-functor.d.ts: -------------------------------------------------------------------------------- 1 | import type { Functor } from "./functor"; 2 | 3 | export interface AsyncFunctor extends Functor { 4 | asyncMap(f: (a: T) => Promise): Promise>; 5 | } 6 | -------------------------------------------------------------------------------- /interfaces/async-monad.d.ts: -------------------------------------------------------------------------------- 1 | import type { Monad } from "./monad"; 2 | import type { AsyncApplicative } from "./async-applicative"; 3 | 4 | export interface AsyncMonad extends AsyncApplicative, Monad { 5 | asyncChain(f: (a: T) => Promise>): Promise>; 6 | } 7 | -------------------------------------------------------------------------------- /interfaces/catamorphism.ts: -------------------------------------------------------------------------------- 1 | export interface Catamorphism { 2 | fold(...args: { [K in keyof Variants]: (value: Variants[K]) => C }): C; 3 | } 4 | -------------------------------------------------------------------------------- /interfaces/class-implements.d.ts: -------------------------------------------------------------------------------- 1 | type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; 2 | 3 | type TupleToUnion = T[number]; 4 | 5 | type TupleToIntersection = UnionToIntersection>; 6 | 7 | export type ClassImplements, I extends any[]> = C; 8 | -------------------------------------------------------------------------------- /interfaces/container.d.ts: -------------------------------------------------------------------------------- 1 | export interface Container { 2 | unwrap(): T; 3 | } 4 | -------------------------------------------------------------------------------- /interfaces/functor.d.ts: -------------------------------------------------------------------------------- 1 | export interface Functor { 2 | map(f: (a: T) => R): Functor; 3 | } 4 | -------------------------------------------------------------------------------- /interfaces/index.d.ts: -------------------------------------------------------------------------------- 1 | export type { Functor } from "./functor"; 2 | export type { AsyncMonad } from "./async-monad"; 3 | export type { Alternative } from "./alternative"; 4 | export type { AsyncFunctor } from "./async-functor"; 5 | export type { AsyncChainable } from "./async-chainable"; 6 | export type { ClassImplements } from "./class-implements"; 7 | export type { AsyncApplicative } from "./async-applicative"; 8 | export type { Monad, MonadConstructor } from "./monad"; 9 | export type { Applicative, ApplicativeConstructor } from "./applicative"; 10 | export type { Container } from "./container"; 11 | export type { Catamorphism } from "./catamorphism"; 12 | -------------------------------------------------------------------------------- /interfaces/monad.d.ts: -------------------------------------------------------------------------------- 1 | import type { Applicative, ApplicativeConstructor } from "./applicative"; 2 | 3 | export interface Monad extends Applicative { 4 | chain(f: (a: T) => Monad): Monad; 5 | join(this: Monad>): Monad; 6 | } 7 | 8 | export interface MonadConstructor extends ApplicativeConstructor { 9 | from(item: I): Monad; 10 | } 11 | -------------------------------------------------------------------------------- /interfaces/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sweet-monads/interfaces", 3 | "version": "3.3.0", 4 | "description": "Monad interfaces", 5 | "exports": { 6 | "types": "./index.d.ts" 7 | }, 8 | "scripts": {}, 9 | "author": "", 10 | "license": "MIT" 11 | } 12 | -------------------------------------------------------------------------------- /iterator/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /iterator/README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads/iterator 2 | 3 | [Iterator additional methods](https://github.com/tc39/proposal-iterator-helpers), Iterator lazy helper methods implementation 4 | 5 | ### This library belongs to _sweet-monads_ project 6 | 7 | > **sweet-monads** — easy-to-use monads implementation with static types definition and separated packages. 8 | 9 | - No dependencies, one small file 10 | - Easily auditable TypeScript/JS code 11 | - Check out all libraries: 12 | [either](https://github.com/JSMonk/sweet-monads/tree/master/either), 13 | [iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator), 14 | [interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces), 15 | [maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 16 | [identity](https://github.com/JSMonk/sweet-monads/tree/master/identity) 17 | 18 | ## Why? 19 | 20 | All of these methods (except methods which return non-`LazyIterator` values) are lazy. They will only consume the iterator when they need the next item from it. 21 | 22 | ```typescript 23 | import LazyIterator from "@sweet-monads/iterator"; 24 | 25 | // array from 1 to 10000 26 | const longArray = Array.from({ length: 10000 }).map((_, i) => i + 1); 27 | const lazyArray = LazyIterator.from(longArray); 28 | 29 | // ~ 25_000 iterations 30 | const sum1 = longArray 31 | .filter(i => i % 2) 32 | .map(i => i * i) 33 | .filter(i => i % 3) 34 | .reduce((sum, i) => sum + i, 0); 35 | 36 | // 10_000 iterations 37 | const sum2 = lazyArray 38 | .filter(i => i % 2) 39 | .map(i => i * i) 40 | .filter(i => i % 3) 41 | .sum(); 42 | ``` 43 | 44 | ## Usage 45 | 46 | > npm install @sweet-monads/iterator 47 | 48 | ```typescript 49 | import LazyIterator from "@sweet-monads/iterator"; 50 | 51 | const lazyArray = LazyIterator.from([1, 2, 3, 4, 5]); 52 | 53 | const newArray = lazyArray.map(a => a * a).collect(); 54 | ``` 55 | 56 | ## API 57 | 58 | - [`LazyIterator.from(iterable)`](#lazyiteratorfrom) 59 | - [`LazyIterator#all`](#lazyiteratorall) 60 | - [`LazyIterator#any`](#lazyiteratorany) 61 | - [`LazyIterator#chain`](#lazyiteratorchain) 62 | - [`LazyIterator#count`](#lazyiteratorcount) 63 | - [`LazyIterator#cycle`](#lazyiteratorcycle) 64 | - [`LazyIterator#enumarate`](#lazyiteratorenumarate) 65 | - [`LazyIterator#fold`](#lazyiteratorfold) 66 | - [`LazyIterator#first`](#lazyiteratorfirst) 67 | - [`LazyIterator#filter`](#lazyiteratorfilter) 68 | - [`LazyIterator#filterMap`](#lazyiteratorfiltermap) 69 | - [`LazyIterator#find`](#lazyiteratorfind) 70 | - [`LazyIterator#findMap`](#lazyiteratorfindmap) 71 | - [`LazyIterator#flatMap`](#lazyiteratorflatmap) 72 | - [`LazyIterator#flatten`](#lazyiteratorflatten) 73 | - [`LazyIterator#forEach`](#lazyiteratorforeach) 74 | - [`LazyIterator#last`](#lazyiteratorlast) 75 | - [`LazyIterator#map`](#lazyiteratormap) 76 | - [`LazyIterator#max`](#lazyiteratormax) 77 | - [`LazyIterator#min`](#lazyiteratormin) 78 | - [`LazyIterator#nth`](#lazyiteratornth) 79 | - [`LazyIterator#partion`](#lazyiteratorpartion) 80 | - [`LazyIterator#position`](#lazyiteratorposition) 81 | - [`LazyIterator#product`](#lazyiteratorproduct) 82 | - [`LazyIterator#reverse`](#lazyiteratorreverse) 83 | - [`LazyIterator#scan`](#lazyiteratorscan) 84 | - [`LazyIterator#skip`](#lazyiteratorskip) 85 | - [`LazyIterator#skipWhile`](#lazyiteratorskipwhile) 86 | - [`LazyIterator#stepBy`](#lazyiteratorstepby) 87 | - [`LazyIterator#sum`](#lazyiteratorsum) 88 | - [`LazyIterator#take`](#lazyiteratortake) 89 | - [`LazyIterator#takeWhile`](#lazyiteratortakewhile) 90 | - [`LazyIterator#unzip`](#lazyiteratorunzip) 91 | - [`LazyIterator#zip`](#lazyiteratorzip) 92 | - [`LazyIterator#compress`](#lazyiteratorcompress) 93 | - [`LazyIterator#permutations`](#lazyiteratorpermutations) 94 | - [`LazyIterator#slice`](#lazyiteratorslice) 95 | - [`LazyIterator#compact`](#lazyiteratorcompact) 96 | - [`LazyIterator#contains`](#lazyiteratorcontains) 97 | - [`LazyIterator#unique`](#lazyiteratorunique) 98 | - [`LazyIterator#isEmpty`](#lazyiteratorisempty) 99 | - [`LazyIterator#except`](#lazyiteratorexcept) 100 | - [`LazyIterator#intersect`](#lazyiteratorintersect) 101 | - [`LazyIterator#prepend`](#lazyiteratorprepend) 102 | - [`LazyIterator#append`](#lazyiteratorappend) 103 | - [`LazyIterator#collect`](#lazyiteratorcollect) 104 | 105 | #### `LazyIterator.from` 106 | 107 | Create `LazyIterator` from any `Iterable` object 108 | 109 | ```typescript 110 | function from(iterable: Iterable): LazyIterator; 111 | function from(iterable: Iterable & { fromIterator: FromIterator }): LazyIterator; 112 | function from(iterable: Iterable, fromIterator: FromIterator): LazyIterator; 113 | ``` 114 | 115 | - `iterable: Iterable` - Iterable object which will be wrapped by `LazyIterator` 116 | - `fromIterator: () => Iterable` (default is `function() { return [...this] }`) - function which define conversion from `LazyIterator` to iterable object (default is `Array`), it could be defined inside `iterable` object. 117 | - Returns `LazyIterator` which contains all elements from `iterable` 118 | 119 | Example: 120 | 121 | ```typescript 122 | LazyIterator.from([1, 2, 3]); // LazyIterator 123 | LazyIterator.from([1, 2, 3]); // LazyIterator 124 | ``` 125 | 126 | #### `LazyIterator.all` 127 | 128 | Tests if every element of the `LazyIterator` matches a predicate. 129 | 130 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 131 | 132 | ```typescript 133 | function all(predicate: (i: I) => boolean): boolean; 134 | ``` 135 | 136 | - `predicate: (i: I) => boolean` - takes a function that returns `true` or `false`. It applies this function to each element of the iterator, and if they all return `true`, then so does `all()`. If any of them return `false`, it returns `false`. 137 | 138 | - Returns `true` if `predicate` return `true` for all elements of `LazyIterator` or `LazyIterator` is empty otherwise `false` 139 | 140 | Example: 141 | 142 | ```typescript 143 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 144 | iterator.all(a => typeof a === "number"); // true 145 | iterator.all(a => a % 2 === 0); // false 146 | ``` 147 | 148 | #### `LazyIterator.any` 149 | 150 | Tests if any element of the `LazyIterator` a predicate. 151 | 152 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 153 | 154 | ```typescript 155 | function any(predicate: (i: I) => boolean): boolean; 156 | ``` 157 | 158 | - `predicate: (i: I) => boolean` - takes a function that returns `true` or `false`. It applies this function to each element of the iterator, and if any of them return `true`, then so does `any()`. If they all return `false`, it returns `false`. 159 | - Returns `true` if exist element from `LazyIterator` for which `predicate` return `true` otherwise `false` 160 | Example: 161 | 162 | ```typescript 163 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 164 | iterator.any(a => a % 2 === 0); // true 165 | iterator.any(a => a === 0); // false 166 | ``` 167 | 168 | #### `LazyIterator.chain` 169 | 170 | Takes two iterators and creates a new iterator over both in sequence. 171 | 172 | ```typescript 173 | function chain(...otherIterators: Array>): LazyIterator; 174 | ``` 175 | 176 | - `iterables: Array>` - array of iterable objects with same type which should be merged in one 177 | - Returns a new `LazyIterator` which will first iterate over values from the first `LazyIterator` and then over values from the second iterator. 178 | 179 | Example: 180 | 181 | ```typescript 182 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 183 | const newIterator = iterator.chain([6, 7]); 184 | 185 | for (const i of newIterator) { 186 | console.log(i); 187 | } 188 | // 1 189 | // 2 190 | // 3 191 | // 4 192 | // 5 193 | // 6 194 | // 7 195 | ``` 196 | 197 | #### `LazyIterator.count` 198 | 199 | Consumes the iterator, counting the number of iterations and returning it. 200 | 201 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 202 | 203 | ```typescript 204 | function count(): number; 205 | ``` 206 | 207 | - Returns count of elements in `LazyIterator` 208 | 209 | Example: 210 | 211 | ```typescript 212 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 213 | const infinityIterator = new LazyIterator(function* () { 214 | while (true) yield 0; 215 | }); 216 | 217 | iterator.count(); // 5 218 | infinityIterator.count(); // Will lock your application 219 | ``` 220 | 221 | #### `LazyIterator.cycle` 222 | 223 | Instead of stopping at `done: true`, the `LazyIterator` will instead start again, from the beginning. After iterating again, it will start at the beginning again. And again. And again. Forever. 224 | 225 | ```typescript 226 | function cycle(): LazyIterator; 227 | ``` 228 | 229 | - Returns repeated an `LazyIterator` endlessly if it is not empty. 230 | 231 | Example: 232 | 233 | ```typescript 234 | const iterator = LazyIterator.from([1, 2, 3]).cycle(); 235 | const empty = LazyIterator.from([]).cycle(); 236 | 237 | let i = iterator[Symbol.iterator](); 238 | 239 | i.next().value; // 1 240 | i.next().value; // 2 241 | i.next().value; // 3 242 | i.next().value; // 1 243 | i.next().value; // 2 244 | i.next().value; // 3 245 | i.next().value; // 1 246 | 247 | let i = empty[Symbol.iterator](); 248 | 249 | i.next().done; // true 250 | 251 | for (const a of iterator); // Will lock your application 252 | for (const a of empty); // Will not computed 253 | ``` 254 | 255 | #### `LazyIterator.enumarate` 256 | 257 | Creates an `LazyIterator` which gives the current iteration count as well as the next value. 258 | 259 | ```typescript 260 | function enumarate(): LazyIterator<[number, I]>; 261 | ``` 262 | 263 | - Returns the `LazyIterator` which yields pairs `(i, val)`, where `i` is the current index of iteration and `val` is the value returned by the iterator. 264 | 265 | Example: 266 | 267 | ```typescript 268 | const iterator = LazyIterator.from([6, 7, 8, 9]).enumerate(); 269 | 270 | for (const [index, element] of iterator) { 271 | console.log(index, element); 272 | } 273 | // 0, 6 274 | // 1, 7 275 | // 2, 8 276 | // 3, 9 277 | ``` 278 | 279 | #### `LazyIterator.fold` 280 | 281 | An `LazyIterator` method that applies a function as long as it returns successfully, producing a single, final value. Folding is useful whenever you have a collection of something, and want to produce a single value from it. 282 | 283 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 284 | 285 | ```typescript 286 | function fold(fn: (a: I, i: I) => I): I; 287 | function fold(fn: (a: A, i: I) => A, accumulator: A): A; 288 | ``` 289 | 290 | - `fn: (a: A, i: I) => A` - accumulator function which fold elements in some value 291 | - `accumulator: A` (default `LazyItertor#first()`) - the initial value the `fn` will have on the first call. 292 | - Returns value which are computed by invocation of `fn` with each element of the `LazyIterator` and `accumulator` which was computed at previous step of iteration. 293 | 294 | Example: 295 | 296 | ```typescript 297 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 298 | 299 | const sum = iterator.fold((sum, i) => sum + i, 0); // number 20 300 | ``` 301 | 302 | #### `LazyIterator.first` 303 | 304 | An `LazyIterator` method that return first element of the `LazyIterator`. 305 | 306 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 307 | 308 | ```typescript 309 | function first(): Maybe; 310 | function first(withoutMaybe: false): Maybe; 311 | function first(withoutMaybe: true): I | undefined; 312 | ``` 313 | 314 | - `withoutMaybe` (default `false`) - regulate return type, if `true` result will be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 315 | - Returns `Maybe.Just` (or `I` if `withoutMaybe` is `true`) if `LazyIterator` is not empty otherwise `Maybe.None` (of `undefined` if `withoutMaybe` is `true`) 316 | 317 | Example: 318 | 319 | ```typescript 320 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 321 | const empty = LazyIterator.from([]); 322 | 323 | const f1 = iterator.first(); // Maybe.Just with value 1 324 | const f2 = iterator.first(false); // Maybe.Just with value 1 325 | const f3 = iterator.first(true); // 1 326 | const f4 = empty.first(); // Maybe.None without value 327 | const f5 = empty.first(false); // Maybe.None without value 328 | const f6 = empty.first(true); // undefined 329 | ``` 330 | 331 | #### `LazyIterator.filter` 332 | 333 | Creates an `LazyIterator` which uses a function to determine if an element should be yielded. 334 | 335 | ```typescript 336 | function filter(predicate: (i: I) => i is T): LazyIterator; 337 | function filter(predicate: (i: I) => boolean): LazyIterator; 338 | ``` 339 | 340 | - `predicate: (i: I) => boolean` - function which must return `true` or `false`. 341 | - Returns `LazyIterator` which calls `fn` function on each element. If `fn` returns `true`, then the element is returned. If `fn` returns `false`, it will try again, and call `fn` on the next element, seeing if it passes the test. 342 | 343 | Example: 344 | 345 | ```typescript 346 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 347 | const filtered = iterator.filter(i => i % 2); // LazyIterator 348 | const twos = iterator.filter((i): i is 2 => i === 2); // LazyIterator<2> 349 | 350 | for (const i of filtered) console.log(i); 351 | // 1 352 | // 3 353 | // 5 354 | // 7 355 | // 9 356 | 357 | for (const i of twos) console.log(i); 358 | // 2 359 | // 2 360 | ``` 361 | 362 | #### `LazyIterator.filterMap` 363 | 364 | Creates an iterator that both filters and maps. 365 | 366 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 367 | 368 | ```typescript 369 | function filterMap(predicateMapper: (i: I) => Maybe): LazyIterator; 370 | function filterMap(predicateMapper: (i: I) => T | undefined): LazyIterator; 371 | ``` 372 | 373 | - `predicateMapper: (i: I) => Maybe | T | undefined` - function which must return an `Maybe`or `T | undefined` if `withoutMaybe` is `true`. 374 | - Returns `LazyIterator` which calls `predicateMapper` on each element. If `predicateMapper` returns `just(element)`, then that element is returned. If `predicateMapper` returns `none`, it will try again, and call `predicateMapper` on the next element, seeing if it will return `just`. 375 | 376 | Example: 377 | 378 | ```typescript 379 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 380 | const filtered = iterator.filterMap(i => (i % 2 ? just(i * i) : none())); // LazyIterator 381 | 382 | // filtered1 <-> [1, 9, 25, 49, 81] 383 | ``` 384 | 385 | #### `LazyIterator.find` 386 | 387 | Searches for an element of an `LazyIterator` that satisfies a predicate. 388 | `find()` is short-circuiting; in other words, it will stop processing as soon as the predicate returns `true`. But, it will lock your application if your `LazyIterator` is cycled and doesn't contain element which will satisfied a predicate. 389 | 390 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 391 | 392 | ```typescript 393 | function find(predicate: (i: I) => boolean): Maybe; 394 | function find(predicate: (i: I) => boolean, withoutMaybe: false): Maybe; 395 | function find(predicate: (i: I) => boolean, withoutMaybe: true): I | undefined; 396 | ``` 397 | 398 | - `predicate: (i: I) => boolean` - function that return `true` or `false`. 399 | - `withoutMaybe` (default `false`) - regulate return type if `true` result should be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 400 | - Returns `Maybe.Just` (or `I` if `withoutMaybe` is `true`) if `LazyIterator` is not empty and contain element for which `predicate` return `true` otherwise `Maybe.None` (of `undefined` if `withoutMaybe` is `true`) 401 | 402 | Example: 403 | 404 | ```typescript 405 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 406 | 407 | const two1 = iterator.find(i => i === 2); // Maybe.Just with value 2 408 | const two2 = iterator.find(i => i === 2, false); // Maybe.Just with value 2 409 | const two3 = iterator.find(i => i === 2, true); // 2 410 | const two4 = iterator.find(i => i === 10); // Maybe.None without value 411 | const two5 = iterator.find(i => i === 10, false); // Maybe.None without value 412 | const two6 = iterator.find(i => i === 10, true); // undefined 413 | ``` 414 | 415 | #### `LazyIterator.findMap` 416 | 417 | Applies function to the elements of `LazyIterator` and returns the first non-none result. 418 | `findMap()` is short-circuiting; in other words, it will stop processing as soon as the predicate returns `Maybe.Just` or non-`undefined` value. But, it will lock your application if your `LazyIterator` is cycled and doesn't contain element which will satisfied a predicate. 419 | 420 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 421 | 422 | ```typescript 423 | function findMap(predicateMapper: (i: I) => Maybe | T | undefined): Maybe; 424 | function findMap(predicateMapper: (i: I) => Maybe | T | undefined, withoutMaybe: false): Maybe; 425 | function findMap(predicateMapper: (i: I) => Maybe | T | undefined, withoutMaybe: true): I | undefined; 426 | ``` 427 | 428 | - `predicateMapper: (i: I) => Maybe | T | undefined` - predicate mapper function which return `Maybe` or `T | undefined`. 429 | - `withoutMaybe` (default `false`) - regulate return type of `predicateMapper`, if `true` result should be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 430 | - Returns mapped by `predicateMapper` `Maybe.Just` (or `I` if `withoutMaybe` is `true`) if `LazyIterator` is not empty and contain element for which `predicateMapper` return `Maybe.Just` (or not `undefined` if `withoutMaybe` is `true`) otherwise `Maybe.None` (of `undefined` if `withoutMaybe` is `true`) 431 | 432 | Example: 433 | 434 | ```typescript 435 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 436 | 437 | const two1 = iterator.find(i => (i === 2 ? just(i) : none())); // Maybe.Just 438 | const two2 = iterator.find(i => (i === 2 ? just(i) : none()), false); // Maybe.Just 439 | const two3 = iterator.find(i => (i === 2 ? i : undefined), true); // 2 440 | const two4 = iterator.find(i => (i === 10 ? just(i) : none())); // Maybe.None 441 | const two5 = iterator.find(i => (i === 10 ? just(i) : none()), false); // Maybe.None 442 | const two6 = iterator.find(i => (i === 10 ? i : undefined), true); // undefined 443 | ``` 444 | 445 | #### `LazyIterator.flatMap` 446 | 447 | Creates an `LazyIterator` that works like map, but flattens nested structure. 448 | 449 | ```typescript 450 | function flatMap(fn: (i: I) => LazyIterator): LazyIterator; 451 | ``` 452 | 453 | - `fn: (i: I) => LazyIterator` - mapper function which return `LazyIterator` 454 | - Returns flattened `LazyIterator` 455 | 456 | Example: 457 | 458 | ```typescript 459 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 460 | const mapped = iterator.flatMap(n => LazyIterator.from("|".repeat(n))); 461 | 462 | for (const i of mapped) console.log(i); 463 | // 15 times "|" 464 | ``` 465 | 466 | #### `LazyIterator.flatten` 467 | 468 | Creates an `LazyIterator` that flattens nested structure. 469 | This is useful when you have an `LazyIterator` of `LazyIterator` or an `LazyIterator` of things that can be turned into iterators and you want to remove one level of indirection. 470 | 471 | ```typescript 472 | function flatten(this: LazyIterator>): LazyIterator; 473 | ``` 474 | 475 | - `this: LazyIterator>` - `this` context should be presented as `LazyIterator` of `LazyIterator` of `I` items. 476 | - Returns flattened at 1 level `LazyIterator` 477 | 478 | Example: 479 | 480 | ```typescript 481 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 482 | 483 | const mapped = iterator.map(n => LazyIterator.from("|".repeat(n))); // LazyIterator>; 484 | const flatten = mapped.flatten(); // LazyIterator 485 | ``` 486 | 487 | #### `LazyIterator.forEach` 488 | 489 | Calls a function on each element of an `LazyIterator`. 490 | This is equivalent to using a `for..of` loop on the `LazyIterator`, although `break` and `continue` are not possible from a function. It's generally more idiomatic to use a `for..of` loop, but `forEach` may be more legible when processing items at the end of longer `LazyIterator` chains. 491 | 492 | ```typescript 493 | function forEach(fn: (i: I) => unknown): void; 494 | ``` 495 | 496 | - `fn: (i: I) => unknown` - function which will be invoked with each element of `LazyIterator` 497 | - Returns `undefined` 498 | 499 | Example: 500 | 501 | ```typescript 502 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 503 | 504 | iterator.forEach(console.log); 505 | // 1 506 | // 2 507 | // 3 508 | // 4 509 | // 5 510 | ``` 511 | 512 | #### `LazyIterator.last` 513 | 514 | An `LazyIterator` method that return last element of the `LazyIterator`. 515 | 516 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 517 | 518 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 519 | 520 | ```typescript 521 | function last(): Maybe; 522 | function last(withoutMaybe: false): Maybe; 523 | function last(withoutMaybe: true): I | undefined; 524 | ``` 525 | 526 | - `withoutMaybe` (default `false`) - regulate return type, if `true` result will be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 527 | - Returns `Maybe.Just` (or `I` if `withoutMaybe` is `true`) if `LazyIterator` is not empty otherwise `Maybe.None` (of `undefined` if `withoutMaybe` is `true`) 528 | 529 | Example: 530 | 531 | ```typescript 532 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 533 | const empty = LazyIterator.from([]); 534 | const cycled = LazyIterator.from([1, 2]).cycle(); 535 | 536 | const f1 = iterator.last(); // Maybe.Just with value 8 537 | const f2 = iterator.last(false); // Maybe.Just with value 8 538 | const f3 = iterator.last(true); // 8 539 | const f4 = empty.last(); // Maybe.None without value 540 | const f5 = empty.last(false); // Maybe.None without value 541 | const f6 = empty.last(true); // undefined 542 | const f7 = cycled.last(true); // Lock your application 543 | ``` 544 | 545 | #### `LazyIterator.map` 546 | 547 | Takes a function and creates an `LazyIterator` which calls that function on each element. `map()` transforms one `LazyIterator` into another. 548 | If you are good at thinking in types, you can think of `map()` like this: If you have an `LazyIterator` that gives you elements of some type `I`, and you want an `LazyIterator` of some other type `T`, you can use `map()`, passing a function that takes an `I` and returns a `T`. 549 | 550 | ```typescript 551 | function map(fn: (i: I) => T): LazyIterator; 552 | ``` 553 | 554 | - `fn: (i: I) => T` - function which will called with each element of `LazyIterator` 555 | - Returns `LazyIterator` which contains all values which was transformed by `fn` 556 | 557 | Example: 558 | 559 | ```typescript 560 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 561 | 562 | const mapped = iterator.map(a => a.toString()); // LazyIterator 563 | 564 | for (const i of mapped) console.log(i); 565 | // "1" 566 | // "1" 567 | // "2" 568 | // "3" 569 | // "5" 570 | // "8" 571 | ``` 572 | 573 | #### `LazyIterator.max` 574 | 575 | Returns the maximum element of an `LazyIterator`. 576 | If several elements are equally maximum, the last element is returned. 577 | 578 | ```typescript 579 | function max(f?: (i: I) => number): Maybe; 580 | function max(f: (i: I) => number, withoutMaybe: false): Maybe; 581 | function max(f: (i: I) => number, withoutMaybe: true): I | undefined; 582 | ``` 583 | 584 | - `fn: (i: I) => T` - function which will convert item `i` in `number`. 585 | - `withoutMaybe` (default `false`) - regulate return type, if `true` result will be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 586 | - Returns maximum element of in `LazyIterator`. If the `LazyIterator` is empty, `Maybe.None` will be returned (or `undefined` if `withoutMaybe` is `true`). 587 | 588 | Example: 589 | 590 | ```typescript 591 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 592 | const empty = LazyIterator.from([]); 593 | 594 | iterator.max(); // Maybe.Just with value 8 595 | iterator.max(a => a); // Maybe.Just with value 8 596 | iterator.max(a => a, false); // Maybe.Just with value 8 597 | iterator.max(a => a, true); // 8 598 | empty.max(); // Maybe.None without value 599 | empty.max(a => a); // Maybe.None without value 600 | empty.max(a => a, false); // Maybe.None without value 601 | empty.max(a => a, true); // undefined 602 | ``` 603 | 604 | #### `LazyIterator.min` 605 | 606 | Returns the minimum element of an `LazyIterator`. 607 | If several elements are equally minimum, the first element is returned. 608 | 609 | ```typescript 610 | function min(f?: (i: I) => number): Maybe; 611 | function min(f: (i: I) => number, withoutMaybe: false): Maybe; 612 | function min(f: (i: I) => number, withoutMaybe: true): I | undefined; 613 | ``` 614 | 615 | - `fn: (i: I) => T` - function which will convert item `i` in `number`. 616 | - `withoutMaybe` (default `false`) - regulate return type, if `true` result will be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 617 | - Returns minimum element of in `LazyIterator`. If the `LazyIterator` is empty, `Maybe.None` will be returned (or `undefined` if `withoutMaybe` is `true`). 618 | 619 | Example: 620 | 621 | ```typescript 622 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 623 | const empty = LazyIterator.from([]); 624 | 625 | iterator.min(); // Maybe.Just with value 1 626 | iterator.min(a => a); // Maybe.Just with value 1 627 | iterator.min(a => a, false); // Maybe.Just with value 1 628 | iterator.min(a => a, true); // 1 629 | empty.min(); // Maybe.None without value 630 | empty.min(a => a); // Maybe.None without value 631 | empty.min(a => a, false); // Maybe.None without value 632 | empty.min(a => a, true); // undefined 633 | ``` 634 | 635 | #### `LazyIterator.nth` 636 | 637 | Returns the `n`th element of the `LazyIterator`. 638 | Like most indexing operations, the count starts from zero, so `nth(0)` returns the first value, `nth(1)` the second, and so on. 639 | 640 | ```typescript 641 | function nth(n: number): Maybe; 642 | function nth(n: number, withoutMaybe: false): Maybe; 643 | function nth(n: number, withoutMaybe: true): I | undefined; 644 | ``` 645 | 646 | - `n: number` - position of element in `LazyIterator` 647 | - `withoutMaybe` (default `false`) - regulate return type, if `true` result will be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 648 | - Returns the `n`th element of the `LazyIterator`. If `n` is greater than or equals to `LazyIterator#count` or `LazyIterator` is empty, `Maybe.None` will be returned (or `undefined` if `withoutMaybe` is `true`). 649 | 650 | Example: 651 | 652 | ```typescript 653 | const iterator = LazyIterator.from([1, 1, 2, 3, 5, 8]); 654 | const empty = LazyIterator.from([]); 655 | 656 | iterator.nth(0); // Maybe.Just with value 1 657 | iterator.nth(0, false); // Maybe.Just with value 1 658 | iterator.nth(0, true); // 1 659 | iterator.nth(6); // Maybe.None without value 660 | iterator.nth(6, false); // Maybe.None without value 661 | iterator.nth(6, true); // undefined 662 | empty.nth(0); // Maybe.None without value 663 | empty.nth(0, false); // Maybe.None without value 664 | empty.nth(0, true); // undefined 665 | ``` 666 | 667 | #### `LazyIterator.partion` 668 | 669 | Returns a 2-elements tuple of arrays. Splits the elements in the input iterable into either of the two arrays. Will fully exhaust the input iterable. The first array contains all items that match the predicate, the second the rest 670 | 671 | ```typescript 672 | function partion(predicate: (i: I) => i is T): [T[], I[]]; 673 | function partion(predicate: (i: I) => boolean): [I[], I[]]; 674 | ``` 675 | 676 | - `predicate: (i: I) => boolean` - function which must return `true` or `false`. 677 | - Returns a 2-elements tuple of arrays which contains elements which satisfy `predicate` (first array) and which not (second array) 678 | 679 | Example: 680 | 681 | ```typescript 682 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 683 | const [filtered, rest] = iterator.partion(i => i % 2); // [number[], number[]] 684 | const [twos, notTwos] = iterator.partion((i): i is 2 => i === 2); // [2[], number[]] 685 | 686 | for (const i of filtered) console.log(i); 687 | // 1 688 | // 3 689 | // 5 690 | // 7 691 | // 9 692 | 693 | for (const i of rest) console.log(i); 694 | // 2 695 | // 2 696 | // 4 697 | // 6 698 | // 8 699 | 700 | for (const i of twos) console.log(i); 701 | // 2 702 | // 2 703 | 704 | for (const i of notTwos) console.log(i); 705 | // 1 706 | // 3 707 | // 4 708 | // 5 709 | // 6 710 | // 7 711 | // 8 712 | // 9 713 | ``` 714 | 715 | #### `LazyIterator.position` 716 | 717 | Searches for an element in an iterator, returning its index. 718 | `position()` is short-circuiting; in other words, it will stop processing as soon as the predicate returns `true`. But, it will lock your application if your `LazyIterator` is cycled and doesn't contain element which will satisfied a predicate. 719 | 720 | _Info:_ more information about [Maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 721 | 722 | ```typescript 723 | function position(predicate: (i: I) => boolean): Maybe; 724 | function position(predicate: (i: I) => boolean, withoutMaybe: false): Maybe; 725 | function position(predicate: (i: I) => boolean, withoutMaybe: true): number | undefined; 726 | ``` 727 | 728 | - `predicate: (i: I) => boolean` - function that return `true` or `false`. 729 | - `withoutMaybe` (default `false`) - regulate return type if `true` result should be "undefinable" item type else `Maybe` which could be presented as `Just` value or `None`. 730 | - Returns `Maybe.Just` (or `I` if `withoutMaybe` is `true`) if `LazyIterator` is not empty and contain element for which `predicate` return `true` otherwise `Maybe.None` (of `undefined` if `withoutMaybe` is `true`) 731 | 732 | Example: 733 | 734 | ```typescript 735 | const iterator = LazyIterator.from([1, 2, 2, 3, 4, 5, 6, 7, 8, 9]); 736 | 737 | const two1 = iterator.position(i => i === 2); // Maybe.Just with value 1 738 | const two2 = iterator.position(i => i === 2, false); // Maybe.Just with value 1 739 | const two3 = iterator.position(i => i === 2, true); // 1 740 | const two4 = iterator.position(i => i === 10); // Maybe.None without value 741 | const two5 = iterator.position(i => i === 10, false); // Maybe.None without value 742 | const two6 = iterator.position(i => i === 10, true); // undefined 743 | ``` 744 | 745 | #### `LazyIterator.product` 746 | 747 | Iterates over the entire iterator, multiplying all the elements. 748 | 749 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 750 | 751 | ```typescript 752 | function product(this: LazyIterator): number; 753 | ``` 754 | 755 | - `this: LazyIterator` - `LazyIterator` should contain `number` elements 756 | - Returns product of each elements in `LazyIterator`. An empty `LazyIterator` returns the one value of the type. 757 | 758 | Example: 759 | 760 | ```typescript 761 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 762 | const empty = LazyIterator.from([]); 763 | const cycled = LazyIterator.from([1]).cycle(); 764 | 765 | const product1 = iterator.product(); // 120 766 | const product2 = empty.product(); // 1 767 | const product3 = cycled.product(); // Will lock your application 768 | ``` 769 | 770 | #### `LazyIterator.reverse` 771 | 772 | Reverses an iterator's direction. Usually, `LazyIterator`s iterate from left to right. After using `reverse()`, an `LazyIterator` will instead iterate from right to left. 773 | 774 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 775 | 776 | ```typescript 777 | function reverse(): LazyIterator; 778 | ``` 779 | 780 | - Returns reverse `LazyIterator` 781 | 782 | Example: 783 | 784 | ```typescript 785 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 786 | const cycled = LazyIterator.from([1]).cycle(); 787 | 788 | const reversed1 = iterator.reverse(); 789 | 790 | for (const a of reversed1) console.log(a); 791 | // 1 792 | // 2 793 | // 3 794 | // 4 795 | // 5 796 | 797 | const reversed2 = cycled.reverse(); // Will lock your application 798 | ``` 799 | 800 | #### `LazyIterator.scan` 801 | 802 | An `LazyIterator` adaptor similar to [`LazyIterator#fold`](#lazyiteratorfold) that holds internal state and produces a new iterator. 803 | 804 | ```typescript 805 | function scan(fn: (a: A, i: I) => A, accumulator: A): A; 806 | ``` 807 | 808 | - `fn: (a: A, i: I) => A` - a function with two arguments, the first being the internal state and the second an `LazyIterator` element. The function can assign to the internal state to share state between iterations. 809 | - `accumulator: A` - an initial value which seeds the internal state 810 | - Returns the `LazyIterator` which yields `accumulator` per each iteration which computed by `fn` invocation. 811 | 812 | Example: 813 | 814 | ```typescript 815 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 816 | const factorials = iterator.scan((a, i) => a * i, 1); 817 | 818 | for (const a of factorials) console.log(a); 819 | // 1 820 | // 2 821 | // 6 822 | // 24 823 | // 120 824 | ``` 825 | 826 | #### `LazyIterator.skip` 827 | 828 | Creates an `LazyIterator` that skips the first `n` elements. 829 | After they have been consumed, the rest of the elements are yielded. 830 | 831 | ```typescript 832 | function skip(n: number): LazyIterator; 833 | ``` 834 | 835 | - `n: number` - count of element which should be skipped. 836 | - Returns the `LazyIterator` which yields all elements after `n` elements. 837 | 838 | Example: 839 | 840 | ```typescript 841 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 842 | const skipped = iterator.skip(2); 843 | 844 | for (const a of skipped) console.log(a); 845 | // 3 846 | // 4 847 | // 5 848 | ``` 849 | 850 | #### `LazyIterator.skipWhile` 851 | 852 | Creates an `LazyIterator` that [`LazyIterator#skip`](#lazyiteratorskip) s elements based on a predicate. 853 | 854 | ```typescript 855 | function skipWhile(predicate: (i: I) => boolean): LazyIterator; 856 | ``` 857 | 858 | - `predicate: (i: I) => boolean` - function which will be called on each element of the `LazyIterator`, and ignore elements until it returns `false`. 859 | - Returns the `LazyIterator` which yields the rest of the elements after first `false` from `predicate` invocation. 860 | 861 | Example: 862 | 863 | ```typescript 864 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 865 | const skipped = iterator.skipWhile(a => a <= 2); 866 | 867 | for (const a of skipped) console.log(a); 868 | // 3 869 | // 4 870 | // 5 871 | ``` 872 | 873 | #### `LazyIterator.stepBy` 874 | 875 | Creates an `LazyIterator` starting at the same point, but stepping by the given amount at each iteration. 876 | 877 | _Note_: The first element of the `LazyIterator` will always be returned, regardless of the step given. 878 | _Note_: If step will be less than 1 then method will throw an Error. 879 | 880 | ```typescript 881 | function stepBy(step: number): LazyIterator; 882 | ``` 883 | 884 | - `step: number` - number (greater than 0) of each element which should be yielded. 885 | - Returns the `LazyIterator` starting at the same point, but stepping by the given amount at each iteration. 886 | 887 | Example: 888 | 889 | ```typescript 890 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 891 | const skipped = iterator.stepBy(2); 892 | 893 | for (const a of skipped) console.log(a); 894 | // 1 895 | // 3 896 | // 5 897 | ``` 898 | 899 | #### `LazyIterator.sum` 900 | 901 | Sums the elements of an iterator. 902 | Takes each element, adds them together, and returns the result. 903 | 904 | _Warning_: Be careful, `LazyIterator` should be iterated for the computation. So it doesn't work with infinity iterable objects. 905 | 906 | ```typescript 907 | function sum(this: LazyIterator): number; 908 | ``` 909 | 910 | - `this: LazyIterator` - `LazyIterator` should contain `number` elements 911 | - Returns sum of each elements in `LazyIterator`. An empty `LazyIterator` returns the zero value of the type. 912 | 913 | Example: 914 | 915 | ```typescript 916 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 917 | const empty = LazyIterator.from([]); 918 | const cycled = LazyIterator.from([1]).cycle(); 919 | 920 | const product1 = iterator.sum(); // 15 921 | const product2 = empty.sum(); // 0 922 | const product3 = cycled.sum(); // Will lock your application 923 | ``` 924 | 925 | #### `LazyIterator.take` 926 | 927 | Creates an `LazyIterator` that yields its first `n` elements. 928 | 929 | ```typescript 930 | function take(n: number): LazyIterator; 931 | ``` 932 | 933 | - `n: number` - count of element which should be taken. 934 | - Returns the `LazyIterator` which yields first `n` elements. 935 | 936 | Example: 937 | 938 | ```typescript 939 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 940 | const skipped = iterator.take(2); 941 | 942 | for (const a of skipped) console.log(a); 943 | // 1 944 | // 2 945 | ``` 946 | 947 | #### `LazyIterator.takeWhile` 948 | 949 | Creates an `LazyIterator` that [`LazyIterator#take`](#lazyiteratortake) s elements based on a predicate. 950 | 951 | ```typescript 952 | function takeWhile(predicate: (i: I) => boolean): LazyIterator; 953 | ``` 954 | 955 | - `predicate: (i: I) => boolean` - function which will be called on each element of the `LazyIterator`, and yield elements until it returns `false`. 956 | - Returns the `LazyIterator` which yields elements before first `false` from `predicate` invocation. 957 | 958 | Example: 959 | 960 | ```typescript 961 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 962 | const skipped = iterator.takeWhile(a => a <= 2); 963 | 964 | for (const a of skipped) console.log(a); 965 | // 1 966 | // 2 967 | ``` 968 | 969 | #### `LazyIterator.unzip` 970 | 971 | Converts an `LazyIterator` of pairs into a pair of arrays. 972 | `unzip()` consumes an entire `LazyIterator` of pairs, producing two arrays: one from the left elements of the pairs, and one from the right elements. 973 | This function is, in some sense, the opposite of [`LazyIterator#zip`](#lazyiteratorzip). 974 | 975 | ```typescript 976 | function unzip(this: LazyIterator<[A, B]>): [A[], B[]]; 977 | ``` 978 | 979 | - `this: LazyIterator<[A, B]>` - the `LazyIterator` which will be an context of the function should contain pair(array with two elements) as element. 980 | - Returns two arrays: first contains elements from the left elements of the pairs, and second contains elements from the right elements. 981 | 982 | Example: 983 | 984 | ```typescript 985 | const iterator = LazyIterator.from([ 986 | [1, 2], 987 | [3, 4], 988 | [5, 6], 989 | [7, 8] 990 | ]); 991 | const [odd, even] = iterator.unzip(); 992 | odd; // [1, 3, 5, 7] 993 | even; // [2, 4, 6, 8] 994 | ``` 995 | 996 | #### `LazyIterator.zip` 997 | 998 | 'Zips up' two iterators into a single `LazyIterator` of pairs. 999 | 1000 | ```typescript 1001 | function zip(other: LazyIterator): LazyIterator<[I, T]>; 1002 | ``` 1003 | 1004 | - `other: LazyIterator` - the `LazyIterator` which elements will be the right element of each yielded pair(array with two elements). 1005 | - Returns `LazyIterator` that will iterate over two other `LazyIterator`s, returning a pair(array with two elements) where the first element comes from the first `LazyIterator` (which was context for the method), and the second element comes from the second `LazyIterator` (which came from argument). 1006 | 1007 | Example: 1008 | 1009 | ```typescript 1010 | const iterator1 = LazyIterator.from([1, 3, 5, 7]); 1011 | const iterator2 = LazyIterator.from([2, 4, 6, 8]); 1012 | const iterator3 = LazyIterator.from([15]); 1013 | 1014 | const zipped1 = iterator1.zip(iterator2); 1015 | const zipped2 = iterator1.zip(iterator3); 1016 | 1017 | for (const a of zipped1) console.log(a); 1018 | // [1, 2] 1019 | // [3, 4] 1020 | // [5, 6] 1021 | // [7, 8] 1022 | 1023 | for (const a of zipped2) console.log(a); 1024 | // [1, 15] 1025 | ``` 1026 | 1027 | #### `LazyIterator.compress` 1028 | 1029 | Pick items for `LazyIterator` by mask which will be provided as argument. 1030 | 1031 | ```typescript 1032 | function compress(mask: number[] | boolean[]): LazyIterator; 1033 | ``` 1034 | 1035 | - `mask: boolean[] | number[]` - mask which determine which elements will be yielded by new `LazyIterator`. 1036 | - Returns `LazyIterator` that filters elements from data returning only those that have a corresponding element in selectors that evaluates to true. Stops when either the data or selectors iterables has been exhausted. 1037 | 1038 | Example: 1039 | 1040 | ```typescript 1041 | const iterator = LazyIterator.from([1, 2, 3, 4, 5, 6, 7, 8, 9]); 1042 | const masked1 = iterator.compress([0, 0, 1, 1, 1, 0, 1]); 1043 | const masked2 = iterator.compress([false, false, true, true, true, false, true]); 1044 | 1045 | for (const a of masked1) console.log(a); 1046 | // 3 1047 | // 4 1048 | // 5 1049 | // 7 1050 | 1051 | for (const a of masked2) console.log(a); 1052 | // 3 1053 | // 4 1054 | // 5 1055 | // 7 1056 | ``` 1057 | 1058 | #### `LazyIterator.permutations` 1059 | 1060 | Return successive permutations of elements in the iterable. 1061 | 1062 | ```typescript 1063 | function permutations(): LazyIterator<[I, I]>; 1064 | ``` 1065 | 1066 | - Returns `LazyIterator` that yield pair (array with two elements) which contains combination of each elements. 1067 | 1068 | Example: 1069 | 1070 | ```typescript 1071 | const iterator = LazyIterator.from([1, 2, 3]); 1072 | const permutated = iterator.permutations(); 1073 | 1074 | for (const a of permutated) console.log(a); 1075 | // [1, 2] 1076 | // [1, 3] 1077 | // [2, 1] 1078 | // [2, 3] 1079 | // [3, 1] 1080 | // [3, 2] 1081 | ``` 1082 | 1083 | #### `LazyIterator.compact` 1084 | 1085 | Create `LazyIterator` without undefined elements. 1086 | 1087 | ```typescript 1088 | function compact(this: LazyIterator): LazyIterator; 1089 | ``` 1090 | 1091 | - Returns `LazyIterator` that contains only non-undefined elements. 1092 | 1093 | Example: 1094 | 1095 | ```typescript 1096 | const iterator = LazyIterator.from([1, 2, 3, undefined, 4, undefined, 15, undefined]); 1097 | const compacted = iterator.compact(); 1098 | 1099 | for (const a of compacted) console.log(a); 1100 | // 1 1101 | // 2 1102 | // 3 1103 | // 4 1104 | // 15 1105 | ``` 1106 | 1107 | #### `LazyIterator.contains` 1108 | 1109 | Tests if any element of the `LazyIterator` matches provided element. 1110 | 1111 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 1112 | 1113 | ```typescript 1114 | function contains(elem: I): boolean; 1115 | ``` 1116 | 1117 | - Returns `LazyIterator` that contains only non-undefined elements. 1118 | 1119 | _Warning_: Be careful, iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 1120 | 1121 | ```typescript 1122 | function contains(elem: I): boolean; 1123 | ``` 1124 | 1125 | - `elem: I` - element which are testing for existing in the `LazyIterator`. 1126 | - Returns `true` if `elem` is existed in `LazyIterator` then return `true` otherwise `false` 1127 | 1128 | Example: 1129 | 1130 | ```typescript 1131 | const iterator = LazyIterator.from([1, 2, 3, 4, 5]); 1132 | iterator.contains(2); // true 1133 | iterator.contains(7); // false 1134 | ``` 1135 | 1136 | #### `LazyIterator.unique` 1137 | 1138 | Create `LazyIterator` which contains only unique elements. 1139 | 1140 | _Warning_: Be careful, iterator should save previous elements for the computation. So it create memory leak with large `LazyIterator`. 1141 | 1142 | ```typescript 1143 | function unique(): LazyIterator; 1144 | ``` 1145 | 1146 | - Returns `LazyIterator` that contains only unique elements. 1147 | 1148 | ```typescript 1149 | const iterator = LazyIterator.from([1, 1, 2, 3, 4, 4, 12, 6, 2, 3, 4, 5]); 1150 | const unique = iterator.unique(); 1151 | 1152 | for (const a of unique) console.log(a); 1153 | // 1 1154 | // 2 1155 | // 3 1156 | // 4 1157 | // 12 1158 | // 6 1159 | // 5 1160 | ``` 1161 | 1162 | #### `LazyIterator.isEmpty` 1163 | 1164 | Check `LazyIterator` for emptiness. 1165 | 1166 | ```typescript 1167 | function isEmpty(): boolean; 1168 | ``` 1169 | 1170 | - Returns `true` if `LazyIterator` doesn't contain any item otherwise `false`. 1171 | 1172 | ```typescript 1173 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5]); 1174 | const iterator2 = LazyIterator.from([]); 1175 | 1176 | iterator1.isEmpty(); // false 1177 | iterator2.isEmpty(); // true 1178 | ``` 1179 | 1180 | #### `LazyIterator.except` 1181 | 1182 | Create `LazyIterator` without element which was contained in provided `LazyIterator`. 1183 | 1184 | _Warning_: Be careful, provided as argument iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 1185 | 1186 | ```typescript 1187 | function except(other: LazyIterator): LazyIterator; 1188 | ``` 1189 | 1190 | - `other: LazyIterator` - `LazyIterator` with element which should be excluded from `this` `LazyIterator` 1191 | - Returns `LazyIterator` that doesn't contain elements from `other` `LazyIterator`. 1192 | 1193 | ```typescript 1194 | const iterator = LazyIterator.from([1, 1, 2, 3, 4, 4, 12, 6, 2, 3, 4, 5]); 1195 | const excepted = iterator.except(LazyIterator.from([2, 4, 1])); 1196 | 1197 | for (const a of excepted) console.log(a); 1198 | // 3 1199 | // 12 1200 | // 6 1201 | // 3 1202 | // 5 1203 | ``` 1204 | 1205 | #### `LazyIterator.intersect` 1206 | 1207 | Create `LazyIterator` only with elements which was existed in both `LazyIterator`s. 1208 | 1209 | _Warning_: Be careful, provided as argument iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 1210 | 1211 | ```typescript 1212 | function except(other: LazyIterator): LazyIterator; 1213 | ``` 1214 | 1215 | - `other: LazyIterator` - `LazyIterator` with element which should be existed in `this` `LazyIterator` 1216 | - Returns `LazyIterator` that contains elements from both `other` and `this` `LazyIterator`. 1217 | 1218 | ```typescript 1219 | const iterator = LazyIterator.from([1, 1, 2, 3, 4, 4, 12, 6, 2, 3, 4, 5]); 1220 | const intersection = iterator.intersect(LazyIterator.from([2, 4, 1, 8, 15])); 1221 | 1222 | for (const a of intesection) console.log(a); 1223 | // 1 1224 | // 1 1225 | // 2 1226 | // 4 1227 | // 4 1228 | // 2 1229 | // 4 1230 | ``` 1231 | 1232 | #### `LazyIterator.isEmpty` 1233 | 1234 | Check `LazyIterator` for emptiness. 1235 | 1236 | ```typescript 1237 | function isEmpty(): boolean; 1238 | ``` 1239 | 1240 | - Returns `true` if `LazyIterator` doesn't contain any item otherwise `false`. 1241 | 1242 | ```typescript 1243 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5]); 1244 | const iterator2 = LazyIterator.from([]); 1245 | 1246 | iterator1.isEmpty(); // false 1247 | iterator2.isEmpty(); // true 1248 | ``` 1249 | 1250 | #### `LazyIterator.except` 1251 | 1252 | Convert `LazyIterator` without element which was contained in provided `LazyIterator`. 1253 | 1254 | _Warning_: Be careful, provided as argument iterator should be iterated for the computation. So it doesn't work with infinity iterable objects. 1255 | 1256 | ```typescript 1257 | function except(other: LazyIterator): LazyIterator; 1258 | ``` 1259 | 1260 | - Returns `Iterable` object which contains all `LazyIterator` elements. 1261 | 1262 | ```typescript 1263 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5], function* (lazy) { 1264 | for (const i of lazy) yield i; 1265 | }); 1266 | const iterator2 = LazyIterator.from([1, 2, 3, 4, 5]); 1267 | 1268 | iterator1.collect(); // iterable object 1269 | iterator2.collect(); // array [1, 2, 3, 4, 5] 1270 | ``` 1271 | 1272 | #### `LazyIterator.prepend` 1273 | 1274 | Create `LazyIterator` with new element at the head position. 1275 | 1276 | ```typescript 1277 | function prepend(item: I): LazyIterator; 1278 | ``` 1279 | 1280 | - `item: I` - element which should be added at the head position of `LazyIterator` 1281 | - Returns `LazyIterator` which contains `item` at the head position. 1282 | 1283 | ```typescript 1284 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5]); 1285 | const iterator2 = iterator1.prepend(4); 1286 | 1287 | for (const a of iterator2) console.log(a); 1288 | // 4 1289 | // 1 1290 | // 2 1291 | // 3 1292 | // 4 1293 | // 5 1294 | ``` 1295 | 1296 | #### `LazyIterator.append` 1297 | 1298 | Create `LazyIterator` with new element in the end. 1299 | 1300 | ```typescript 1301 | function append(item: I): LazyIterator; 1302 | ``` 1303 | 1304 | - `item: I` - element which should be added at the end position of `LazyIterator` 1305 | - Returns `LazyIterator` which contains `item` at the end position. 1306 | 1307 | ```typescript 1308 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5]); 1309 | const iterator2 = iterator1.append(4); 1310 | 1311 | for (const a of iterator2) console.log(a); 1312 | // 1 1313 | // 2 1314 | // 3 1315 | // 4 1316 | // 5 1317 | // 4 1318 | ``` 1319 | 1320 | #### `LazyIterator.collect` 1321 | 1322 | Convert `LazyIterator` in initial iterable object or array. 1323 | If initial iterable object had `fromIterator` method or `fromIterator` was provided as second argument of [`LazyIterator.from`](#lazyiteratorfrom) function `fromIterator` will be called for converting, otherwise convert `LazyIterator` in array. 1324 | 1325 | ```typescript 1326 | function collect(): Iterable; 1327 | ``` 1328 | 1329 | - Returns `Iterable` object which contains all `LazyIterator` elements. 1330 | 1331 | ```typescript 1332 | const iterator1 = LazyIterator.from([1, 2, 3, 4, 5], function* (lazy) { 1333 | for (const i of lazy) yield i; 1334 | }); 1335 | const iterator2 = LazyIterator.from([1, 2, 3, 4, 5]); 1336 | 1337 | iterator1.collect(); // iterable object 1338 | iterator2.collect(); // array [1, 2, 3, 4, 5] 1339 | ``` 1340 | 1341 | ## License 1342 | 1343 | MIT (c) Artem Kobzar see LICENSE file. 1344 | -------------------------------------------------------------------------------- /iterator/filter-operation.ts: -------------------------------------------------------------------------------- 1 | import { just, none } from "@sweet-monads/maybe"; 2 | import { IntermidiateOperation } from "./intermediate-operation"; 3 | import type { Maybe } from "@sweet-monads/maybe"; 4 | 5 | export class FilterOperation extends IntermidiateOperation { 6 | constructor(private readonly predicate: (v: A, terminate: () => void) => v is B) { 7 | super(); 8 | } 9 | 10 | execute(value: A): Maybe { 11 | const result = this.predicate(value, () => this.terminate()); 12 | if (this.terminated) { 13 | return none(); 14 | } 15 | return result ? just(value as B) : none(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iterator/index.ts: -------------------------------------------------------------------------------- 1 | import { MapOperation } from "./map-operation"; 2 | import { FilterOperation } from "./filter-operation"; 3 | import { IntermidiateOperation } from "./intermediate-operation"; 4 | import MaybeConstructor, { Maybe, just, none } from "@sweet-monads/maybe"; 5 | 6 | type FromIterator = Iterable, T extends Iterable = Iterable> = (source: S) => T; 7 | 8 | const defaultFromIterator = function (original: Iterable): Array { 9 | return [...original]; 10 | }; 11 | 12 | const id: any = (x: number) => x; 13 | 14 | export default class LazyIterator implements Iterable { 15 | static from(iterable: Iterable | LazyIterator) { 16 | return iterable instanceof LazyIterator ? iterable : new LazyIterator(iterable); 17 | } 18 | 19 | private static withOperation( 20 | from: LazyIterator, 21 | isCycled: boolean, 22 | ...operations: Array> 23 | ): LazyIterator { 24 | return new LazyIterator( 25 | // @ts-expect-error Not sure why 26 | from.source, 27 | [...from.operations, ...operations], 28 | isCycled 29 | ); 30 | } 31 | 32 | private constructor( 33 | private readonly source: Iterable, 34 | private readonly operations: Array> = [], 35 | private readonly isCycled: boolean = false 36 | ) {} 37 | 38 | /* Intermediate Operations */ 39 | append(item: I) { 40 | return this.chain([item]); 41 | } 42 | 43 | chain(...otherIterators: Array>): LazyIterator { 44 | return LazyIterator.from([this, ...otherIterators]).flatMap(LazyIterator.from); 45 | } 46 | 47 | compact(this: LazyIterator) { 48 | return this.filter((a: I | undefined): a is I => a !== undefined); 49 | } 50 | 51 | compress(mask: number[] | boolean[]) { 52 | let index = 0; 53 | return this.filter(() => Boolean(mask[index++])); 54 | } 55 | 56 | cycle() { 57 | return new LazyIterator(this.source, this.operations, true); 58 | } 59 | 60 | enumarate() { 61 | let index = 0; 62 | return this.map((item): [number, I] => [index++, item]); 63 | } 64 | 65 | except(other: LazyIterator) { 66 | const existed = new Set(other); 67 | return this.filter(value => !existed.has(value)); 68 | } 69 | 70 | filter(predicate: (i: I) => i is T): LazyIterator; 71 | filter(predicate: (i: I) => boolean): LazyIterator; 72 | filter(predicate: (i: I) => i is T): LazyIterator { 73 | return LazyIterator.withOperation( 74 | this, 75 | this.isCycled, 76 | new FilterOperation((v): v is T => predicate(v)) 77 | ); 78 | } 79 | 80 | filterMap(predicateMapper: (i: I) => Maybe): LazyIterator; 81 | filterMap(predicateMapper: (i: I) => T | undefined): LazyIterator; 82 | filterMap(predicateMapper: (i: I) => Maybe | T | undefined) { 83 | return LazyIterator.withOperation( 84 | this, 85 | this.isCycled, 86 | new MapOperation(i => predicateMapper(i)), 87 | new FilterOperation((v: I): v is I => (v instanceof MaybeConstructor ? v.isJust() : v !== undefined)), 88 | new MapOperation(v => (v instanceof MaybeConstructor ? v.value : v)) 89 | ); 90 | } 91 | 92 | flatten(this: LazyIterator>): LazyIterator { 93 | return this.flatMap(x => x as LazyIterator); 94 | } 95 | 96 | flatMap(fn: (i: I) => LazyIterator): LazyIterator { 97 | return LazyIterator.withOperation(this, this.isCycled, new MapOperation(x => fn(x), true)); 98 | } 99 | 100 | intersect(other: LazyIterator): LazyIterator { 101 | const existed = new Set(other); 102 | return this.filter(value => existed.has(value)); 103 | } 104 | 105 | map(fn: (i: I) => T): LazyIterator { 106 | return LazyIterator.withOperation( 107 | this, 108 | this.isCycled, 109 | new MapOperation(x => fn(x)) 110 | ); 111 | } 112 | 113 | permutations(): LazyIterator<[I, I]> { 114 | return this.enumarate().flatMap(([i, x1]) => 115 | this.enumarate().filterMap(([j, x2]) => (i !== j ? just([x1, x2]) : none())) 116 | ); 117 | } 118 | 119 | prepend(item: I): LazyIterator { 120 | return LazyIterator.from([item]).chain(this); 121 | } 122 | 123 | reverse(): LazyIterator { 124 | if (this.isCycled) { 125 | throw new Error("Reverse can work only at finite iterable sequence."); 126 | } 127 | const oldIterator = this instanceof Array ? this : [...this]; 128 | let i = oldIterator.length - 1; 129 | return this.map(() => oldIterator[i--]); 130 | } 131 | 132 | skip(n: number): LazyIterator { 133 | return this.filter(() => n === 0 || n-- <= 0); 134 | } 135 | 136 | scan(fn: (a: A, i: I) => A, accumulator: A): LazyIterator { 137 | return this.map(value => (accumulator = fn(accumulator, value))); 138 | } 139 | 140 | skipWhile(predicate: (i: I) => boolean): LazyIterator { 141 | let hasBeenFalse = false; 142 | return this.filter(i => hasBeenFalse || (hasBeenFalse = !predicate(i))); 143 | } 144 | 145 | slice(start = 0, end?: number): LazyIterator { 146 | let index = 0; 147 | return LazyIterator.withOperation( 148 | this, 149 | end === undefined && this.isCycled, 150 | new FilterOperation((_, terminate): _ is I => { 151 | if (end !== undefined && index >= end) { 152 | terminate(); 153 | return false; 154 | } 155 | return index++ >= start; 156 | }) 157 | ); 158 | } 159 | 160 | stepBy(step: number) { 161 | if (step <= 0) { 162 | throw new Error("step should be greater than 0"); 163 | } 164 | 165 | let n = 0; 166 | 167 | return this.filter(() => { 168 | if (--n <= 0) { 169 | n = step; 170 | return true; 171 | } 172 | return false; 173 | }); 174 | } 175 | 176 | take(n: number) { 177 | return LazyIterator.withOperation( 178 | this, 179 | false, 180 | new FilterOperation((_, terminate): _ is I => { 181 | if (n-- <= 0) { 182 | terminate(); 183 | return false; 184 | } 185 | return true; 186 | }) 187 | ); 188 | } 189 | 190 | takeWhile(predicate: (i: I) => boolean) { 191 | return LazyIterator.withOperation( 192 | this, 193 | false, 194 | new FilterOperation((x, terminate): x is I => { 195 | if (!predicate(x)) { 196 | terminate(); 197 | return false; 198 | } 199 | return true; 200 | }) 201 | ); 202 | } 203 | 204 | unique() { 205 | const existedValues = new Set(); 206 | return this.filter(value => { 207 | if (existedValues.has(value)) { 208 | return false; 209 | } 210 | existedValues.add(value); 211 | return true; 212 | }); 213 | } 214 | 215 | zip(other: LazyIterator): LazyIterator<[I, T]> { 216 | const otherIterator = other[Symbol.iterator](); 217 | return LazyIterator.withOperation( 218 | this, 219 | this.isCycled, 220 | new MapOperation((first, terminate) => { 221 | const second = otherIterator.next(); 222 | if (second.done) { 223 | terminate(); 224 | } 225 | return [first, second.value]; 226 | }) 227 | ); 228 | } 229 | /* Terminal Operations */ 230 | 231 | collect(fromIterator: FromIterator = defaultFromIterator): Iterable { 232 | if (typeof fromIterator !== "function") { 233 | throw new Error("fromIteartor should be implemented as function for lazy iterating"); 234 | } 235 | return fromIterator(this); 236 | } 237 | 238 | public *[Symbol.iterator](): Iterator { 239 | let isEmpty = true; 240 | do { 241 | outer: for (let value of this.source) { 242 | isEmpty = false; 243 | for (let i = 0; i < this.operations.length; i++) { 244 | const operation = this.operations[i]; 245 | const maybeValue = operation.execute(value); 246 | if (operation.isTerminated) { 247 | return; 248 | } 249 | if (maybeValue.isNone()) { 250 | continue outer; 251 | } 252 | value = maybeValue.value as I; 253 | if (operation.isFlat && value instanceof LazyIterator) { 254 | yield* LazyIterator.withOperation( 255 | value, 256 | value.isCycled, 257 | ...(this.operations.slice(i + 1) as IntermidiateOperation[]) 258 | ); 259 | continue outer; 260 | } 261 | } 262 | yield value; 263 | } 264 | } while (this.isCycled && !isEmpty); 265 | } 266 | 267 | forEach(fn: (i: I) => void | Maybe) { 268 | if (this.isCycled) { 269 | throw new Error("All terminated methods will be infinity executed!"); 270 | } 271 | outer: for (let value of this.source) { 272 | for (let i = 0; i < this.operations.length; i++) { 273 | const operation = this.operations[i]; 274 | const maybeValue = operation.execute(value); 275 | if (operation.isTerminated) { 276 | return; 277 | } 278 | if (maybeValue.isNone()) { 279 | continue outer; 280 | } 281 | value = maybeValue.value as I; 282 | if (operation.isFlat && value instanceof LazyIterator) { 283 | LazyIterator.withOperation( 284 | value, 285 | value.isCycled, 286 | ...(this.operations.slice(i + 1) as IntermidiateOperation[]) 287 | ).forEach(fn); 288 | continue outer; 289 | } 290 | } 291 | const result = fn(value); 292 | if (result instanceof MaybeConstructor && result.isNone()) { 293 | return; 294 | } 295 | } 296 | } 297 | 298 | all(this: LazyIterator, predicate: (i: I) => i is T): this is LazyIterator; 299 | all(predicate: (i: I) => boolean): boolean; 300 | all(predicate: (i: I) => boolean) { 301 | let result = true; 302 | // eslint-disable-next-line no-cond-assign 303 | this.forEach(value => ((result = predicate(value)) ? just(undefined) : none())); 304 | return result; 305 | } 306 | 307 | any(predicate: (i: I) => boolean) { 308 | let result = false; 309 | // eslint-disable-next-line no-cond-assign 310 | this.forEach(value => ((result = predicate(value)) ? none() : just(undefined))); 311 | return result; 312 | } 313 | 314 | count() { 315 | return this.fold(c => c + 1, 0); 316 | } 317 | 318 | contains(elem: I) { 319 | return this.any(a => a === elem); 320 | } 321 | 322 | find(predicate: (i: I) => i is T): Maybe; 323 | find(predicate: (i: I) => i is T, withoutMaybe: false): Maybe; 324 | find(predicate: (i: I) => i is T, withoutMaybe: true): T | undefined; 325 | find(predicate: (i: I) => boolean): Maybe; 326 | find(predicate: (i: I) => boolean, withoutMaybe: false): Maybe; 327 | find(predicate: (i: I) => boolean, withoutMaybe: true): I | undefined; 328 | find(predicate: (i: I) => boolean, withoutMaybe = false) { 329 | let foundValue = none(); 330 | this.forEach(value => { 331 | if (predicate(value)) { 332 | foundValue = just(value); 333 | return none(); 334 | } 335 | return just(undefined); 336 | }); 337 | return withoutMaybe ? foundValue.value : foundValue; 338 | } 339 | 340 | findMap(predicateMapper: (i: I) => Maybe | T | undefined): Maybe; 341 | findMap(predicateMapper: (i: I) => Maybe | T | undefined, withoutMaybe: false): Maybe; 342 | findMap(predicateMapper: (i: I) => Maybe | T | undefined, withoutMaybe: true): I | undefined; 343 | findMap(predicateMapper: (i: I) => Maybe | T | undefined, withoutMaybe = false) { 344 | let result = none(); 345 | 346 | this.forEach(item => { 347 | const maybeItem = predicateMapper(item); 348 | if ((maybeItem instanceof MaybeConstructor && maybeItem.isNone()) || maybeItem === undefined) { 349 | return just(undefined); 350 | } 351 | result = maybeItem instanceof MaybeConstructor ? maybeItem : just(maybeItem as T); 352 | return none(); 353 | }); 354 | 355 | return withoutMaybe ? result.value : result; 356 | } 357 | 358 | first(): Maybe; 359 | first(withoutMaybe: false): Maybe; 360 | first(withoutMaybe: true): I | undefined; 361 | first(withoutMaybe = false): Maybe | I | undefined { 362 | const first = this[Symbol.iterator]().next(); 363 | if (withoutMaybe) { 364 | return first.value; 365 | } 366 | return first.done ? none() : just(first.value); 367 | } 368 | 369 | fold(fn: (a: I, i: I) => I): I; 370 | fold(fn: (a: A, i: I) => A, accumulator: A): A; 371 | fold(fn: (a: A | I, i: I) => A | I, accumulator?: A | I) { 372 | this.forEach(value => { 373 | if (accumulator === undefined) { 374 | accumulator = value; 375 | } else { 376 | accumulator = fn(accumulator, value); 377 | } 378 | }); 379 | return accumulator; 380 | } 381 | 382 | isEmpty(): boolean { 383 | return this.first().isNone(); 384 | } 385 | 386 | last(): Maybe; 387 | last(withoutMaybe: false): Maybe; 388 | last(withoutMaybe: true): I | undefined; 389 | last(withoutMaybe = false) { 390 | const result = this.fold((_, a) => just(a), none()); 391 | return withoutMaybe ? result.value : result; 392 | } 393 | 394 | max(this: LazyIterator): Maybe; 395 | max(f: (i: I) => number): Maybe; 396 | max(f: (i: I) => number, withoutMaybe: false): Maybe; 397 | max(f: (i: I) => number, withoutMaybe: true): I | undefined; 398 | max(getValue = id, withoutMaybe = false): Maybe | I | undefined { 399 | const res = this.fold( 400 | (max, a) => just(max.isNone() || getValue(a) >= getValue(max.value) ? a : (max.value as I)), 401 | none() 402 | ); 403 | return withoutMaybe ? res.value : res; 404 | } 405 | 406 | min(this: LazyIterator): Maybe; 407 | min(f?: (i: I) => number): Maybe; 408 | min(f: (i: I) => number, withoutMaybe: false): Maybe; 409 | min(f: (i: I) => number, withoutMaybe: true): I | undefined; 410 | min(getValue = id, withoutMaybe = false): Maybe | I | undefined { 411 | const res = this.fold( 412 | (max, a) => just(max.isNone() || getValue(a) < getValue(max.value) ? a : (max.value as I)), 413 | none() 414 | ); 415 | return withoutMaybe ? res.value : res; 416 | } 417 | 418 | nth(n: number): Maybe; 419 | nth(n: number, withoutMaybe: false): Maybe; 420 | nth(n: number, withoutMaybe: true): I | undefined; 421 | nth(n: number, withoutMaybe = false) { 422 | let result = none(); 423 | this.forEach(value => { 424 | if (n-- <= 0) { 425 | result = just(value); 426 | return none(); 427 | } 428 | return just(undefined); 429 | }); 430 | return withoutMaybe ? result.value : result; 431 | } 432 | 433 | partion(predicate: (i: I) => i is T): [T[], I[]]; 434 | partion(predicate: (i: I) => boolean): [I[], I[]]; 435 | partion(predicate: (i: I) => boolean): [I[], I[]] { 436 | return this.fold( 437 | ([left, right], value) => { 438 | if (predicate(value)) { 439 | left.push(value); 440 | } else { 441 | right.push(value); 442 | } 443 | return [left, right]; 444 | }, 445 | [[] as I[], [] as I[]] 446 | ); 447 | } 448 | 449 | position(predicate: (i: I) => boolean): Maybe; 450 | position(predicate: (i: I) => boolean, withoutMaybe: false): Maybe; 451 | position(predicate: (i: I) => boolean, withoutMaybe: true): number | undefined; 452 | position(predicate: (i: I) => boolean, withoutMaybe = false) { 453 | let index = 0; 454 | let hasFound = false; 455 | this.forEach(value => { 456 | if (predicate(value)) { 457 | hasFound = true; 458 | return none(); 459 | } 460 | index++; 461 | return just(undefined); 462 | }); 463 | const result = hasFound ? just(index) : none(); 464 | return withoutMaybe ? result.value : result; 465 | } 466 | 467 | product(this: LazyIterator): number { 468 | return this.fold((res, a) => res * a, 1); 469 | } 470 | 471 | sum(this: LazyIterator): number { 472 | return this.fold((sum, a) => sum + a, 0); 473 | } 474 | 475 | unzip(this: LazyIterator<[A, B]>): [A[], B[]] { 476 | const left: A[] = []; 477 | const right: B[] = []; 478 | return this.fold( 479 | ([left, right], [a, b]) => { 480 | left.push(a); 481 | right.push(b); 482 | return [left, right]; 483 | }, 484 | [left as A[], right as B[]] 485 | ); 486 | } 487 | 488 | get [Symbol.toStringTag]() { 489 | return this.constructor.name; 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /iterator/intermediate-operation.ts: -------------------------------------------------------------------------------- 1 | import type { Maybe } from "@sweet-monads/maybe"; 2 | 3 | export abstract class IntermidiateOperation { 4 | protected terminated = false; 5 | 6 | constructor(public readonly isFlat = false) {} 7 | 8 | public get isTerminated(): boolean { 9 | return this.terminated; 10 | } 11 | 12 | abstract execute(v: A): Maybe; 13 | 14 | protected terminate() { 15 | this.terminated = true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iterator/map-operation.ts: -------------------------------------------------------------------------------- 1 | import { just } from "@sweet-monads/maybe"; 2 | import { IntermidiateOperation } from "./intermediate-operation"; 3 | import type { Maybe } from "@sweet-monads/maybe"; 4 | 5 | export class MapOperation extends IntermidiateOperation { 6 | constructor(fn: (v: A, terminate: () => void) => B); 7 | constructor(fn: (v: A, terminate: () => void) => Iterable, isFlat: true); 8 | constructor(private readonly fn: (v: A, terminate: () => void) => B, isFlat = false) { 9 | super(isFlat); 10 | } 11 | 12 | execute(value: A): Maybe { 13 | return just(this.fn(value, () => this.terminate())); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /iterator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sweet-monads/iterator", 3 | "version": "3.3.1", 4 | "description": "Implement iterator with helper functions", 5 | "main": "./cjs/index.js", 6 | "module": "./esm/index.js", 7 | "exports": { 8 | "types": "./index.d.ts", 9 | "import": "./esm/index.js", 10 | "require": "./cjs/index.js" 11 | }, 12 | "homepage": "https://github.com/JSMonk/sweet-monads/tree/master/iterator", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/JSMonk/sweet-monads/tree/master/iterator" 16 | }, 17 | "scripts": { 18 | "build": "run-s build:pre build:all build:after", 19 | "build:clean": "rm -rf build && mkdir build", 20 | "build:config": "cp ../tsconfig.json tsconfig.json", 21 | "build:pre": "run-p build:clean build:config", 22 | "build:esm": "tsc --project ./tsconfig.json --module 'ESNext' --outDir './build/esm'", 23 | "build:cjs": "tsc --project ./tsconfig.json --module 'CommonJS' --outDir './build/cjs'", 24 | "build:declaration": "tsc --project ./tsconfig.json --outDir './build' --emitDeclarationOnly", 25 | "build:all": "run-p build:esm build:cjs build:declaration", 26 | "build:copy": "cp ./package.json ./build/package.json && cp ./README.md ./build/README.md", 27 | "build:fixcjs": "echo '{\"type\":\"commonjs\"}' > ./build/cjs/package.json", 28 | "build:fixesm": "echo '{\"type\":\"module\"}' > ./build/esm/package.json", 29 | "build:after": "run-p build:copy build:fixcjs build:fixesm" 30 | }, 31 | "author": "JSMonk", 32 | "license": "MIT", 33 | "dependencies": { 34 | "@sweet-monads/maybe": "^3.3.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ["tests"], 3 | transform: { 4 | "^.+\\.ts$": "ts-jest" 5 | }, 6 | globals: { 7 | "ts-jest": { 8 | tsconfig: "./tsconfig.json" 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /maybe/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Artem Kobzar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /maybe/README.md: -------------------------------------------------------------------------------- 1 | # @sweet-monads/maybe 2 | 3 | [Maybe Monad](https://en.wikibooks.org/wiki/Haskell/Understanding_monads/Maybe), The Maybe monad represents computations which might "go wrong" by not returning a value. 4 | 5 | ### This library belongs to _sweet-monads_ project 6 | 7 | > **sweet-monads** — easy-to-use monads implementation with static types definition and separated packages. 8 | 9 | - No dependencies, one small file 10 | - Easily auditable TypeScript/JS code 11 | - Check out all libraries: 12 | [either](https://github.com/JSMonk/sweet-monads/tree/master/either), 13 | [iterator](https://github.com/JSMonk/sweet-monads/tree/master/iterator), 14 | [interfaces](https://github.com/JSMonk/sweet-monads/tree/master/interfaces), 15 | [maybe](https://github.com/JSMonk/sweet-monads/tree/master/maybe) 16 | [identity](https://github.com/JSMonk/sweet-monads/tree/master/identity) 17 | 18 | ## Usage 19 | 20 | > npm install @sweet-monads/maybe 21 | 22 | ```typescript 23 | import { Maybe, just, none } from "@sweet-monads/maybe"; 24 | 25 | type User = { email: string; password: string }; 26 | 27 | function getUser(id: number): Maybe { 28 | return just({ email: "test@gmail.com", password: "test" }); 29 | } 30 | 31 | // Maybe 32 | const user = getUser(1).map(({ email }) => email); 33 | ``` 34 | 35 | ## API 36 | 37 | - [`chain`](#chain) 38 | - [`merge`](#merge) 39 | - [`none`](#none) 40 | - [`just`](#just) 41 | - [`from`](#from) 42 | - [`fromNullable`](#fromnullable) 43 | - [`isMaybe`](#ismaybe) 44 | - [`Maybe#isNone`](#maybeisnone) 45 | - [`Maybe#isJust`](#maybeisjust) 46 | - [`Maybe#or`](#maybeor) 47 | - [`Maybe#join`](#maybejoin) 48 | - [`Maybe#map`](#maybemap) 49 | - [`Maybe#asyncMap`](#maybeasyncmap) 50 | - [`Maybe#apply`](#maybeapply) 51 | - [`Maybe#asyncApply`](#maybeasyncapply) 52 | - [`Maybe#chain`](#maybechain) 53 | - [`Maybe#asyncChain`](#maybeasyncchain) 54 | - [`Maybe#fold`](#maybefold) 55 | - [Helpers](#helpers) 56 | 57 | #### `chain` 58 | 59 | ```typescript 60 | function chain(fn: (v: A) => Promise>): (m: Maybe) => Promise>; 61 | ``` 62 | 63 | - `fn: (v: A) => Promise>` - function which should be applied asynchronously to `Maybe` value 64 | - Returns function with `Maybe` argument and promised `Maybe` with `Maybe.None` or mapped by `fn` value (could be used inside `Promise#then` function). 65 | 66 | Example: 67 | 68 | ```typescript 69 | const getValue = async () => just(1); 70 | 71 | // Maybe 72 | const result = await getValue() 73 | .then(Maybe.chain(async v => just(v * 2))) 74 | .then(Maybe.chain(async v => none())); 75 | ``` 76 | 77 | #### `merge` 78 | 79 | ```typescript 80 | function merge(values: [Maybe]): Maybe<[V1]>; 81 | function merge(values: [Maybe, Maybe]): Maybe<[V1, V2]>; 82 | function merge(values: [Maybe, Maybe, Maybe]): Maybe<[V1, V2, V3]>; 83 | // ... until 10 elements 84 | ``` 85 | 86 | - `values: Array>` - Array of Maybe values which will be merged into Maybe of Array 87 | - Returns `Maybe>` Maybe of Array which will contain `Just>` if all of array elements was `Just` else `None`. 88 | 89 | Example: 90 | 91 | ```typescript 92 | const v1 = just(2); // Maybe.Just 93 | const v2 = just("test"); // Maybe.Just 94 | const v3 = none(); // Maybe.None 95 | 96 | merge([v1, v2]); // Maybe<[number, string]>.Just 97 | merge([v1, v2, v3]); // Maybe<[number, string, boolean]>.None 98 | ``` 99 | 100 | #### `none` 101 | 102 | ```typescript 103 | function none(): Maybe; 104 | ``` 105 | 106 | - Returns `Maybe` with `None` state 107 | Example: 108 | 109 | ```typescript 110 | const v1 = none(); // Maybe.None 111 | const v2 = none(); // Maybe.None 112 | ``` 113 | 114 | #### `just` 115 | 116 | ```typescript 117 | function just(value: T): Maybe; 118 | ``` 119 | 120 | - Returns `Maybe` with `Just` state which contain value with `T` type. 121 | Example: 122 | 123 | ```typescript 124 | const v1 = just(2); // Maybe.Just 125 | const v2 = just<2>(2); // Maybe<2>.Just 126 | ``` 127 | 128 | #### `from` 129 | 130 | The same as [`just`](#just) 131 | 132 | ```typescript 133 | function from(value: T): Maybe; 134 | ``` 135 | 136 | - Returns `Maybe` with `Just` state which contain value with `T` type. 137 | Example: 138 | 139 | ```typescript 140 | const v1 = from(2); // Maybe.Just 141 | const v2 = from<2>(2); // Maybe<2>.Just 142 | ``` 143 | 144 | #### `fromNullable` 145 | 146 | ```typescript 147 | function fromNullable(value: T): Maybe>; 148 | ``` 149 | 150 | - Returns `Maybe` with `Just` state which contain value with `T` type if value is not null or undefined and `None` otherwise. 151 | Example: 152 | 153 | ```typescript 154 | const v1 = fromNullable(2); // Maybe.Just 155 | ``` 156 | 157 | #### `isMaybe` 158 | 159 | ```typescript 160 | function isMaybe(value: unknown | Maybe): value is Maybe; 161 | ``` 162 | 163 | - Returns `boolean` if given `value` is instance of Maybe constructor. 164 | Example: 165 | 166 | ```typescript 167 | const value: unknown = 2; 168 | if (isMaybe(value)) { 169 | // ... value is Maybe at this block 170 | } 171 | ``` 172 | 173 | #### `Maybe#isNone` 174 | 175 | ```typescript 176 | function isNone(): boolean; 177 | ``` 178 | 179 | - Returns `true` if state of `Maybe` is `None` otherwise `false` 180 | Example: 181 | 182 | ```typescript 183 | const v1 = just(2); 184 | const v2 = none(); 185 | 186 | v1.isNone(); // false 187 | v2.isNone(); // true 188 | ``` 189 | 190 | #### `Maybe#isJust` 191 | 192 | ```typescript 193 | function isJust(): boolean; 194 | ``` 195 | 196 | - Returns `true` if state of `Maybe` is `Just` otherwise `false` 197 | Example: 198 | 199 | ```typescript 200 | const v1 = just(2); 201 | const v2 = none(); 202 | 203 | v1.isJust(); // true 204 | v2.isJust(); // false 205 | ``` 206 | 207 | #### `Maybe#or` 208 | 209 | ```typescript 210 | function or(x: Maybe): Maybe; 211 | ``` 212 | 213 | - Returns `Maybe`. If state of `this` is `Just` then `this` will be returned otherwise `x` argument will be returned 214 | Example: 215 | 216 | ```typescript 217 | const v1 = just(1); 218 | const v2 = none(); 219 | const v3 = none(); 220 | const v4 = just(4); 221 | 222 | v1.or(v2); // v1 will be returned 223 | v2.or(v1); // v1 will be returned 224 | v2.or(v3); // v3 will be returned 225 | v1.or(v4); // v1 will be returned 226 | 227 | v2.or(v3).or(v1); // v1 will be returned 228 | v2.or(v1).or(v3); // v1 will be returned 229 | v1.or(v2).or(v3); // v1 will be returned 230 | ``` 231 | 232 | #### `Maybe#join` 233 | 234 | ```typescript 235 | function join(this: Maybe>): Maybe; 236 | ``` 237 | 238 | - `this: Maybe>` - `Maybe` instance which contains other `Maybe` instance as `Just` value. 239 | - Returns unwrapped `Maybe` - if current `Maybe` has `Just` state and inner `Maybe` has `Just` state then returns inner `Maybe` `Just`, otherwise returns `Maybe` `None`. 240 | Example: 241 | 242 | ```typescript 243 | const v1 = just(just(2)); 244 | const v2 = just(none()); 245 | const v3 = none>(); 246 | 247 | v1.join(); // Maybe.Just with value 2 248 | v2.join(); // Maybe.None without value 249 | v3.join(); // Maybe.None without value 250 | ``` 251 | 252 | #### `Maybe#map` 253 | 254 | ```typescript 255 | function map(fn: (val: Val) => NewVal): Maybe; 256 | ``` 257 | 258 | - Returns mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 259 | Example: 260 | 261 | ```typescript 262 | const v1 = just(2); 263 | const v2 = none(); 264 | 265 | const newVal1 = v1.map(a => a.toString()); // Maybe.Just with value "2" 266 | const newVal2 = v2.map(a => a.toString()); // Maybe.None without value 267 | ``` 268 | 269 | #### `Maybe#mapNullable` 270 | 271 | ```typescript 272 | function mapNullable(fn: (val: Val) => (NewVal | null | undefined)): Maybe>; 273 | ``` 274 | 275 | - Returns mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` and the returned value is not `null` or `undefined` otherwise `None` 276 | Example: 277 | 278 | ```typescript 279 | const v1 = just(2); 280 | const v2 = none(); 281 | 282 | const newVal1 = v1.mapNullable(a => a.toString()); // Maybe.Just with value "2" 283 | const newVal2 = v2.mapNullable(a => a.toString()); // Maybe.None without value 284 | const newVal3 = v2.mapNullable(a => null); // Maybe.None without value 285 | const newVal4 = v2.mapNullable(a => undefined); // Maybe.None without value 286 | ``` 287 | 288 | #### `Maybe#mapNullable` 289 | 290 | ```typescript 291 | function map(fn: (val: Val) => NewVal): Maybe; 292 | ``` 293 | 294 | - Returns mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 295 | Example: 296 | 297 | ```typescript 298 | const v1 = just(2); 299 | const v2 = none(); 300 | 301 | const newVal1 = v1.map(a => a.toString()); // Maybe.Just with value "2" 302 | const newVal2 = v2.map(a => a.toString()); // Maybe.None without value 303 | ``` 304 | 305 | ##### `Maybe#asyncMap` 306 | 307 | ```typescript 308 | function asyncMap(fn: (val: Val) => Promise): Promise>; 309 | ``` 310 | 311 | - Returns `Promise` with mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 312 | Example: 313 | 314 | ```typescript 315 | const v1 = just(2); 316 | const v2 = none(); 317 | 318 | // Promise.Just> with value "2" 319 | const newVal1 = v1.asyncMap(a => Promise.resolve(a.toString())); 320 | // Promise.None> without value 321 | const newVal2 = v2.asyncMap(a => Promise.resolve(a.toString())); 322 | ``` 323 | 324 | ##### `Maybe#apply` 325 | 326 | ```typescript 327 | function apply(this: Maybe<(a: A) => B>, arg: Maybe): Maybe; 328 | function apply(this: Maybe, fn: Maybe<(a: A) => B>): Maybe; 329 | ``` 330 | 331 | - `this | fn` - function wrapped by Maybe, which should be applied to value `arg` 332 | - `arg | this` - value which should be applied to `fn` 333 | - Returns mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 334 | Example: 335 | 336 | ```typescript 337 | const v1 = just(2); 338 | const v2 = none(); 339 | const fn1 = just((a: number) => a * 2); 340 | const fn2 = none<(a: number) => number>(); 341 | 342 | const newVal1 = fn1.apply(v1); // Maybe.Just with value 4 343 | const newVal2 = fn1.apply(v2); // Maybe.None without value 344 | const newVal3 = fn2.apply(v1); // Maybe.None without value 345 | const newVal4 = fn2.apply(v2); // Maybe.None without value 346 | ``` 347 | 348 | ##### `Maybe#asyncApply` 349 | 350 | Async variant of [`Maybe#apply`](#maybeapply) 351 | 352 | ```typescript 353 | function asyncApply( 354 | this: Maybe<(a: Promise | A) => Promise>, 355 | arg: Maybe | A> 356 | ): Promise>; 357 | function asyncApply(this: Maybe | A>, fn: Maybe<(a: Promise | A) => Promise>): Promise>; 358 | ``` 359 | 360 | - `this | fn` - function wrapped by Maybe, which should be applied to value `arg` 361 | - `arg | this` - value which should be applied to `fn` 362 | - Returns `Promise` with mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 363 | Example: 364 | 365 | ```typescript 366 | const v1 = just(2); 367 | const v2 = none(); 368 | const fn1 = just((a: number) => Promise, resolve(a * 2)); 369 | const fn2 = none<(a: number) => Promise>(); 370 | 371 | const newVal1 = fn1.apply(v1); // Promise.Just> with value 4 372 | const newVal2 = fn1.apply(v2); // Promise.None> without value 373 | const newVal3 = fn2.apply(v1); // Promise.None> without value 374 | const newVal4 = fn2.apply(v2); // Promise.None> without value 375 | ``` 376 | 377 | #### `Maybe#chain` 378 | 379 | ```typescript 380 | function chain(fn: (val: Val) => Maybe): Maybe; 381 | ``` 382 | 383 | - Returns mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` and returned by `fn` value is `Just` too otherwise `None` 384 | Example: 385 | 386 | ```typescript 387 | const v1 = just(2); 388 | const v2 = none(); 389 | 390 | const newVal1 = v1.chain(a => just(a.toString())); // Maybe.Just with value "2" 391 | const newVal2 = v1.chain(a => none()); // Maybe.None without value 392 | const newVal3 = v2.chain(a => just(a.toString())); // Maybe.None without value 393 | const newVal4 = v2.chain(a => none()); // Maybe.None without value 394 | ``` 395 | 396 | ##### `Maybe#asyncChain` 397 | 398 | ```typescript 399 | function asyncChain(fn: (val: Val) => Promise>): Promise>; 400 | ``` 401 | 402 | - Returns `Promise` with mapped by `fn` function value wrapped by `Maybe` if `Maybe` is `Just` otherwise `None` 403 | Example: 404 | 405 | ```typescript 406 | const v1 = just(2); 407 | const v2 = none(); 408 | // Promise>.Just with value "2" 409 | const newVal1 = v1.asyncChain(a => Promise.resolve(just(a.toString()))); 410 | // Promise>.None without value 411 | const newVal2 = v1.asyncChain(a => Promise.resolve(none())); 412 | // Promise>.None without value 413 | const newVal3 = v2.asyncChain(a => Promise.resolve(just(a.toString()))); 414 | // Promise>.None without value 415 | const newVal4 = v2.asyncChain(a => Promise.resolve(none())); 416 | ``` 417 | 418 | ##### `Maybe#fold` 419 | 420 | ```typescript 421 | function fold(mapNone: () => C, mapJust: (value: T) => C): C; 422 | ``` 423 | 424 | - Returns value mapped by one of mapper functions. If `Maybe` is `Just` then `mapJust` is result of `mapJust` 425 | is returned, otherwise return of `mapNone` gets returned. 426 | 427 | Example: 428 | ```typescript 429 | const v1 = just(2); 430 | const v2 = none(); 431 | 432 | // "just: 4" 433 | const newVal1 = v1.fold(() => 'none', value => 'just: '+value*2) 434 | // "none" 435 | const newVal2 = v1.fold(() => 'none', value => 'just: '+value*2) 436 | ``` 437 | 438 | #### Helpers 439 | 440 | ```typescript 441 | // Value from Maybe instance 442 | const { value } = just(2); // number | undefined 443 | ``` 444 | 445 | ```typescript 446 | just(2).unwrap(); // returns 2 447 | none().unwrap(); // Throws error 448 | 449 | just(2).unwrap(() => new Error("NEVER!")); // returns 2 450 | none().unwrap(() => new CustomError("My error")); // Throws CustomError 451 | 452 | just(2).unwrapOr(3) // returns 3 453 | none().unwrapOr(3) // returns 2 454 | 455 | just(2).unwrapOrElse(num => num * 2) // returns 4 456 | none().unwrapOrElse(num => num * 2) // returns 2 457 | ``` 458 | 459 | ## License 460 | 461 | MIT (c) Artem Kobzar see LICENSE file. 462 | -------------------------------------------------------------------------------- /maybe/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AsyncMonad, 3 | Alternative, 4 | AsyncChainable, 5 | ClassImplements, 6 | MonadConstructor, 7 | ApplicativeConstructor, 8 | Container, 9 | Catamorphism 10 | } from "@sweet-monads/interfaces"; 11 | 12 | const enum MaybeState { 13 | Just = "Just", 14 | None = "None" 15 | } 16 | 17 | function isWrappedFunction(m: Maybe | Maybe<(a: A) => B>): m is Maybe<(a: A) => B> { 18 | return typeof m.value === "function"; 19 | } 20 | 21 | function isWrappedAsyncFunction( 22 | m: Maybe> | Maybe<(a: A) => B | Promise> 23 | ): m is Maybe<(a: A) => B> { 24 | return typeof m.value === "function"; 25 | } 26 | 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 28 | type StaticCheck = ClassImplements< 29 | typeof MaybeConstructor, 30 | [MonadConstructor, ApplicativeConstructor, AsyncChainable>] 31 | >; 32 | export default class MaybeConstructor 33 | implements AsyncMonad, Alternative, Container, Catamorphism<[void, T]> { 34 | static chain(f: (v: A) => Promise>) { 35 | return (m: Maybe): Promise> => m.asyncChain(f); 36 | } 37 | 38 | static merge(values: [Maybe]): Maybe<[V1]>; 39 | static merge(values: [Maybe, Maybe]): Maybe<[V1, V2]>; 40 | static merge(values: [Maybe, Maybe, Maybe]): Maybe<[V1, V2, V3]>; 41 | static merge(values: [Maybe, Maybe, Maybe, Maybe]): Maybe<[V1, V2, V3, V4]>; 42 | static merge( 43 | values: [Maybe, Maybe, Maybe, Maybe, Maybe] 44 | ): Maybe<[V1, V2, V3, V4, V5]>; 45 | static merge( 46 | values: [Maybe, Maybe, Maybe, Maybe, Maybe, Maybe] 47 | ): Maybe<[V1, V2, V3, V4, V5, V6]>; 48 | static merge( 49 | values: [Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe] 50 | ): Maybe<[V1, V2, V3, V4, V5, V6, V7]>; 51 | static merge( 52 | values: [Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe] 53 | ): Maybe<[V1, V2, V3, V4, V5, V6, V7, V8]>; 54 | static merge( 55 | values: [Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe] 56 | ): Maybe<[V1, V2, V3, V4, V5, V6, V7, V8, V9]>; 57 | static merge( 58 | values: [ 59 | Maybe, 60 | Maybe, 61 | Maybe, 62 | Maybe, 63 | Maybe, 64 | Maybe, 65 | Maybe, 66 | Maybe, 67 | Maybe, 68 | Maybe 69 | ] 70 | ): Maybe<[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10]>; 71 | static merge(maybies: Array>): Maybe; 72 | static merge(maybies: Array>): Maybe { 73 | return maybies.reduce( 74 | (res: Maybe, v) => res.chain(res => v.map(v => res.concat([v]))), 75 | MaybeConstructor.just([]) 76 | ); 77 | } 78 | 79 | static from(v: T): Maybe { 80 | return MaybeConstructor.just(v); 81 | } 82 | 83 | static fromNullable(v: T): Maybe> { 84 | return v != null ? MaybeConstructor.just(v as NonNullable) : MaybeConstructor.none>(); 85 | } 86 | 87 | private static _noneInstance: MaybeConstructor; 88 | 89 | static none(): Maybe { 90 | if (MaybeConstructor._noneInstance === undefined) { 91 | MaybeConstructor._noneInstance = new MaybeConstructor(MaybeState.None, undefined); 92 | } 93 | return MaybeConstructor._noneInstance; 94 | } 95 | 96 | static just(v: T): Maybe { 97 | return new MaybeConstructor(MaybeState.Just, v); 98 | } 99 | 100 | private constructor(private readonly type: S, public readonly value: S extends MaybeState.Just ? T : undefined) {} 101 | 102 | isNone(): this is MaybeConstructor { 103 | return this.type === MaybeState.None; 104 | } 105 | 106 | isJust(): this is MaybeConstructor { 107 | return this.type === MaybeState.Just; 108 | } 109 | 110 | join(this: Maybe>): Maybe { 111 | return this.chain(x => x); 112 | } 113 | 114 | map(f: (r: T) => V): Maybe { 115 | if (this.isJust()) { 116 | return MaybeConstructor.just(f(this.value)); 117 | } 118 | return MaybeConstructor.none(); 119 | } 120 | 121 | mapNullable(f: (r: T) => V | null | undefined): Maybe> { 122 | if (this.isJust()) { 123 | const result = f(this.value); 124 | if (result == null) return MaybeConstructor.none(); 125 | return MaybeConstructor.just(result); 126 | } 127 | 128 | return MaybeConstructor.none(); 129 | } 130 | 131 | asyncMap(f: (r: T) => Promise): Promise> { 132 | if (this.isJust()) { 133 | return f(this.value).then(MaybeConstructor.just); 134 | } 135 | return Promise.resolve(MaybeConstructor.none()); 136 | } 137 | 138 | apply(this: Maybe<(a: A) => B>, arg: Maybe): Maybe; 139 | apply(this: Maybe, fn: Maybe<(a: A) => B>): Maybe; 140 | apply(this: Maybe | Maybe<(a: A) => B>, argOrFn: Maybe | Maybe<(a: A) => B>): Maybe { 141 | if (this.isNone() || argOrFn.isNone()) { 142 | return MaybeConstructor.none(); 143 | } 144 | if (isWrappedFunction(this)) { 145 | return (argOrFn as Maybe).map(this.value as (a: A) => B); 146 | } 147 | if (isWrappedFunction(argOrFn)) { 148 | return (argOrFn as Maybe<(a: A) => B>).apply(this as Maybe); 149 | } 150 | throw new Error("Some of the arguments should be a function"); 151 | } 152 | 153 | asyncApply(this: Maybe<(a: A) => Promise>, arg: Maybe | A>): Promise>; 154 | asyncApply(this: Maybe | A>, fn: Maybe<(a: A) => Promise>): Promise>; 155 | asyncApply( 156 | this: Maybe | A> | Maybe<(a: A) => Promise>, 157 | argOrFn: Maybe | A> | Maybe<(a: A) => Promise> 158 | ): Promise> { 159 | if (this.isNone() || argOrFn.isNone()) { 160 | return Promise.resolve(MaybeConstructor.none()); 161 | } 162 | if (isWrappedAsyncFunction(this)) { 163 | return (argOrFn as Maybe | A>) 164 | .map(a => Promise.resolve(a)) 165 | .asyncMap(pa => pa.then(this.value as (a: A) => Promise)); 166 | } 167 | if (isWrappedAsyncFunction(argOrFn)) { 168 | return (argOrFn as Maybe<(a: A) => Promise>).asyncApply(this as Maybe>); 169 | } 170 | throw new Error("Some of the arguments should be a function"); 171 | } 172 | 173 | chain(f: (r: T) => Maybe): Maybe { 174 | if (this.isJust()) { 175 | return f(this.value as T); 176 | } 177 | return MaybeConstructor.none(); 178 | } 179 | 180 | asyncChain(f: (r: T) => Promise>): Promise> { 181 | if (this.isJust()) { 182 | return f(this.value as T); 183 | } 184 | return Promise.resolve(MaybeConstructor.none()); 185 | } 186 | 187 | or(x: Maybe): Maybe { 188 | return this.isNone() ? x : (this as Maybe); 189 | } 190 | 191 | unwrap(errorFactory: () => unknown = () => new Error("Value is None")): T { 192 | if (this.isJust()) return this.value; 193 | throw errorFactory(); 194 | } 195 | 196 | unwrapOr(x: T): T { 197 | return this.isJust() ? this.value : x; 198 | } 199 | 200 | unwrapOrElse(f: () => T): T { 201 | return this.isJust() ? this.value : f(); 202 | } 203 | 204 | fold(mapNone: (value: undefined) => C, mapJust: (r: T) => C) { 205 | return this.isJust() ? mapJust(this.value as T) : mapNone(undefined); 206 | } 207 | 208 | get [Symbol.toStringTag]() { 209 | return "Maybe"; 210 | } 211 | } 212 | 213 | export type Maybe = MaybeConstructor | MaybeConstructor; 214 | 215 | export const { merge, just, none, from, fromNullable, chain } = MaybeConstructor; 216 | 217 | export const isMaybe = (value: unknown | Maybe): value is Maybe => value instanceof MaybeConstructor; 218 | -------------------------------------------------------------------------------- /maybe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sweet-monads/maybe", 3 | "version": "3.3.1", 4 | "description": "", 5 | "main": "./cjs/index.js", 6 | "module": "./esm/index.js", 7 | "exports": { 8 | "types": "./index.d.ts", 9 | "import": "./esm/index.js", 10 | "require": "./cjs/index.js" 11 | }, 12 | "homepage": "https://github.com/JSMonk/sweet-monads/tree/master/maybe", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/JSMonk/sweet-monads/tree/master/maybe" 16 | }, 17 | "scripts": { 18 | "build": "run-s build:pre build:all build:after", 19 | "build:clean": "rm -rf build && mkdir build", 20 | "build:config": "cp ../tsconfig.json tsconfig.json", 21 | "build:pre": "run-p build:clean build:config", 22 | "build:esm": "tsc --project ./tsconfig.json --module 'ESNext' --outDir './build/esm'", 23 | "build:cjs": "tsc --project ./tsconfig.json --module 'CommonJS' --outDir './build/cjs'", 24 | "build:declaration": "tsc --project ./tsconfig.json --outDir './build' --emitDeclarationOnly", 25 | "build:all": "run-p build:esm build:cjs build:declaration", 26 | "build:copy": "cp ./package.json ./build/package.json && cp ./README.md ./build/README.md", 27 | "build:fixcjs": "echo '{\"type\":\"commonjs\"}' > ./build/cjs/package.json", 28 | "build:fixesm": "echo '{\"type\":\"module\"}' > ./build/esm/package.json", 29 | "build:after": "run-p build:copy build:fixcjs build:fixesm" 30 | }, 31 | "dependencies": { 32 | "@sweet-monads/interfaces": "^3.3.0" 33 | }, 34 | "author": "", 35 | "license": "MIT" 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sweet-monads", 3 | "version": "3.3.0", 4 | "description": "", 5 | "main": "index.js", 6 | "private": true, 7 | "directories": { 8 | "test": "tests" 9 | }, 10 | "workspaces": [ 11 | "either", 12 | "interfaces", 13 | "iterator", 14 | "maybe", 15 | "identity" 16 | ], 17 | "scripts": { 18 | "test": "jest", 19 | "format": "prettier --write .", 20 | "lint": "eslint .", 21 | "prepare": "husky install", 22 | "build:either": "yarn workspace @sweet-monads/either build", 23 | "build:maybe": "yarn workspace @sweet-monads/maybe build", 24 | "build:identity": "yarn workspace @sweet-monads/identity build", 25 | "build:iterator": "yarn workspace @sweet-monads/iterator build", 26 | "build": "yarn build:either && yarn build:maybe && yarn build:identity && yarn build:iterator", 27 | "publint:either": "publint either/build", 28 | "publint:maybe": "publint maybe/build", 29 | "publint:identity": "publint identity/build", 30 | "publint:interfaces": "publint interfaces", 31 | "publint:iterator": "publint iterator/build", 32 | "publint": "yarn publint:interfaces && yarn publint:either && yarn publint:maybe && yarn publint:identity && yarn publint:iterator" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/JSMonk/sweet-monads.git" 37 | }, 38 | "author": "JSMonk", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/JSMonk/sweet-monads/issues" 42 | }, 43 | "husky": { 44 | "hooks": { 45 | "pre-commit": "lint-staged" 46 | } 47 | }, 48 | "lint-staged": { 49 | "*.ts": [ 50 | "prettier --write", 51 | "eslint --quiet" 52 | ], 53 | "*.md": "yaspeller-ci" 54 | }, 55 | "homepage": "https://github.com/JSMonk/sweet-monads#readme", 56 | "devDependencies": { 57 | "@types/jest": "^26.0.19", 58 | "@typescript-eslint/eslint-plugin": "^4.14.2", 59 | "@typescript-eslint/parser": "^4.14.2", 60 | "eslint": "^7.19.0", 61 | "eslint-config-prettier": "7.2.0", 62 | "fast-check": "^2.10.0", 63 | "husky": "^8.0.1", 64 | "jest": "^29.7.0", 65 | "lint-staged": "10.5.3", 66 | "npm-run-all": "^4.1.5", 67 | "prettier": "2.2.1", 68 | "publint": "^0.1.11", 69 | "ts-jest": "^29.1.5", 70 | "typescript": "^4.1.3", 71 | "yaspeller-ci": "^1.0.2" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/either.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from "fast-check"; 2 | import { Either, left, merge, right, fromTry, fromPromise } from "@sweet-monads/either"; 3 | 4 | describe("Either", () => { 5 | test("merge generic", () => 6 | fc.assert( 7 | fc.property(fc.subarray(["1", "2", "3"]), fc.subarray(["4", "5", "6"]), (l, r) => { 8 | const mergedOne = merge([...l.map(x => left(x)), ...r.map(y => right(y))]); 9 | expect(mergedOne.isLeft()).toBe(l.length > 0); 10 | expect(mergedOne.isRight()).toBe(l.length === 0); 11 | 12 | const mergedTwo = merge([...r.map(y => right(y)), ...l.map(x => left(x))]); 13 | expect(mergedTwo.isLeft()).toBe(l.length > 0); 14 | expect(mergedTwo.isRight()).toBe(l.length === 0); 15 | }) 16 | )); 17 | 18 | test("merge concrete", () => { 19 | const v1 = right(2); 20 | const v2 = right("test"); 21 | const v3 = left(new Error()); 22 | const v4 = left(new Int8Array()); 23 | const v5 = left(new Float32Array()); 24 | 25 | expect(merge([v1, v2]).value).toStrictEqual([2, "test"]); 26 | expect(merge([v1, v2, v3]).value).toBeInstanceOf(Error); 27 | }); 28 | test("join", () => { 29 | const v1 = right(right(2)); 30 | const v2 = right(left(new Error())); 31 | const v3 = left>(new TypeError()); 32 | 33 | const r1 = v1.join(); 34 | const r2 = v2.join(); 35 | const r3 = v3.join(); 36 | expect(r1.isRight()); 37 | if (r1.isRight()) { 38 | expect(r1.value).toBe(2); 39 | } 40 | expect(r2.isLeft()); 41 | if (r2.isLeft()) { 42 | expect(r2.value).toBeInstanceOf(Error); 43 | } 44 | 45 | expect(r3.isLeft()); 46 | if (r3.isLeft()) { 47 | expect(r3.value).toBeInstanceOf(TypeError); 48 | } 49 | }); 50 | 51 | test("asyncApply", () => { 52 | const v1 = right(2); 53 | const v2 = left(new Error()); 54 | const fn1 = right Promise>((a: number) => Promise.resolve(a * 2)); 55 | const fn2 = left Promise>(new Error()); 56 | }); 57 | 58 | test("unwrap", () => { 59 | const v1 = right(2); 60 | const v2 = left(new TypeError()); 61 | 62 | expect(v1.unwrap()).toBe(2); 63 | expect(() => v2.unwrap()).toThrow(new Error("Either state is Left")); 64 | 65 | expect(v1.unwrap(() => new TypeError("NEVER!"))).toBe(2); 66 | expect(() => v2.unwrap(x => x)).toThrow(v2.value as TypeError); 67 | }); 68 | 69 | test("unwrapOr", () => { 70 | const v1 = right(2); 71 | const v2 = left(new Error()); 72 | 73 | expect(v1.unwrapOr(3)).toBe(2); 74 | expect(v2.unwrapOr(3)).toBe(3); 75 | }); 76 | 77 | test("unwrapOrElse", () => { 78 | const v1 = right(2); 79 | const v2 = left(3); 80 | 81 | expect(v1.unwrapOrElse(x => x * 2)).toBe(2); 82 | expect(v2.unwrapOrElse(x => x * 2)).toBe(6); 83 | }); 84 | 85 | test("fromTry", () => { 86 | const error = new Error("test"); 87 | const v1 = fromTry(() => 1); 88 | const v2 = fromTry(() => { 89 | throw error; 90 | }); 91 | 92 | expect(v1.unwrap()).toBe(1); 93 | expect(v2.value).toBe(error); 94 | expect(v2.isLeft()).toBe(true); 95 | }); 96 | test("fromPromise", async () => { 97 | const error = new Error("test"); 98 | const v1 = await fromPromise(Promise.resolve(1)); 99 | const v2 = await fromPromise(Promise.reject(error)); 100 | 101 | expect(v1.unwrap()).toBe(1); 102 | expect(v2.value).toBe(error); 103 | expect(v2.isLeft()).toBe(true); 104 | }); 105 | test("toString", () => { 106 | expect(right(1).toString()).toBe("[object Either]"); 107 | expect(left(1).toString()).toBe("[object Either]"); 108 | }); 109 | 110 | test("fold", () => { 111 | const mapLeft = (value: number) => { 112 | return value * 2; 113 | }; 114 | const mapRight = (value: number) => { 115 | return value * 3; 116 | }; 117 | expect(right(2).fold(mapLeft, mapRight)).toBe(6); 118 | expect(left(1).fold(mapLeft, mapRight)).toBe(2); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /tests/identity.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from "fast-check"; 2 | import { from } from "@sweet-monads/identity"; 3 | import { join } from "path"; 4 | import { fileURLToPath } from "url"; 5 | 6 | describe("Identity", () => { 7 | test("map", () => { 8 | const file = from("file://") 9 | .map(fileURLToPath) 10 | .map(x => join("etc", "hosts")); 11 | 12 | expect(file.unwrap()).toBe("etc/hosts"); 13 | }); 14 | test("toString", () => { 15 | expect(from(1).toString()).toBe("[object Identity]"); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tests/iterator.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from "fast-check"; 2 | import Maybe from "@sweet-monads/maybe"; 3 | import LazyIterator from "@sweet-monads/iterator"; 4 | 5 | describe("Iterator", () => { 6 | test("toString", () => { 7 | expect(LazyIterator.from([]).toString()).toBe("[object LazyIterator]"); 8 | }); 9 | describe("all", () => { 10 | it("should say true for all items", () => { 11 | fc.assert( 12 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 13 | const iterator: LazyIterator = LazyIterator.from(arr); 14 | expect(iterator.all(a => typeof a === "number")).toBe(true); 15 | }) 16 | ); 17 | }); 18 | it("should say false for all items", () => { 19 | fc.assert( 20 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 21 | const iterator: LazyIterator = LazyIterator.from(arr); 22 | expect(iterator.all(a => typeof a === "string")).toBe(arr.length === 0); 23 | }) 24 | ); 25 | }); 26 | }); 27 | describe("any", () => { 28 | it("should say true for any item", () => { 29 | fc.assert( 30 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 31 | const iterator: LazyIterator = LazyIterator.from(arr); 32 | expect(iterator.any(a => typeof a === "number")).toBe(arr.length !== 0); 33 | }) 34 | ); 35 | }); 36 | it("should say false for any item", () => { 37 | fc.assert( 38 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 39 | const iterator: LazyIterator = LazyIterator.from(arr); 40 | expect(iterator.any(a => typeof a === "string")).toBe(false); 41 | }) 42 | ); 43 | }); 44 | }); 45 | describe("chain", () => { 46 | it("should merge two iterators in one lazy", () => { 47 | fc.assert( 48 | fc.property(fc.array(fc.nat()), fc.array(fc.nat()), (arr1: number[], arr2: number[]) => { 49 | const iterator: LazyIterator = LazyIterator.from(arr1).chain(arr2); 50 | expect([...iterator]).toEqual([...arr1, ...arr2]); 51 | expect(iterator).toBeInstanceOf(LazyIterator); 52 | }) 53 | ); 54 | }); 55 | }); 56 | describe("count", () => { 57 | it("should give right count of items", () => { 58 | fc.assert( 59 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 60 | const iterator: LazyIterator = LazyIterator.from(arr); 61 | expect(iterator.count()).toBe(arr.length); 62 | expect(iterator).toBeInstanceOf(LazyIterator); 63 | }) 64 | ); 65 | }); 66 | }); 67 | describe("cycle", () => { 68 | it("should create infinity link", () => { 69 | fc.assert( 70 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 71 | const iterator: LazyIterator = LazyIterator.from(arr).cycle(); 72 | if (arr.length === 0) { 73 | expect(iterator[Symbol.iterator]().next().done).toBe(true); 74 | } else { 75 | let neededIndex = arr.length * 3; 76 | let lastItem; 77 | let firstItem; 78 | for (const item of iterator) { 79 | if (firstItem === undefined) { 80 | firstItem = item; 81 | } 82 | if (neededIndex-- < 0) { 83 | break; 84 | } 85 | lastItem = item; 86 | } 87 | expect(lastItem).toBe(firstItem); 88 | } 89 | }) 90 | ); 91 | }); 92 | }); 93 | describe("enumarate", () => { 94 | it("should create enumarated iterator", () => { 95 | fc.assert( 96 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 97 | const iterator = LazyIterator.from(arr).enumarate(); 98 | for (const [index, item] of iterator) { 99 | expect(item).toBe(arr[index]); 100 | } 101 | expect(iterator.count()).toBe(arr.length); 102 | }) 103 | ); 104 | }); 105 | }); 106 | describe("fold", () => { 107 | it("should do right folding", () => { 108 | fc.assert( 109 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 110 | const sum = (s: number, a: number) => s + a; 111 | const concat = (s: string, a: number) => s + a; 112 | const iterator: LazyIterator = LazyIterator.from(arr); 113 | if (arr.length !== 0) { 114 | expect(iterator.fold(sum)).toEqual(arr.reduce(sum)); 115 | } 116 | expect(iterator.fold(concat, "")).toEqual(arr.reduce(concat, "")); 117 | }) 118 | ); 119 | }); 120 | }); 121 | describe("first", () => { 122 | it("should get right first element with Maybe", () => { 123 | fc.assert( 124 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 125 | const iterator: LazyIterator = LazyIterator.from(arr); 126 | const first = iterator.first(); 127 | expect(first).toEqual(arr[0] === undefined ? Maybe.none() : Maybe.just(arr[0])); 128 | }) 129 | ); 130 | }); 131 | it("should get right first element", () => { 132 | fc.assert( 133 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 134 | const iterator: LazyIterator = LazyIterator.from(arr); 135 | expect(iterator.first(true)).toBe(arr[0]); 136 | }) 137 | ); 138 | }); 139 | }); 140 | describe("filter", () => { 141 | it("should get right filtered elements", () => { 142 | fc.assert( 143 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 144 | const criteria = (item: number) => item < 3; 145 | const iterator: LazyIterator = LazyIterator.from(arr); 146 | expect([...iterator.filter(criteria)]).toEqual([...arr.filter(criteria)]); 147 | }) 148 | ); 149 | }); 150 | }); 151 | describe("filterMap", () => { 152 | it("should get right filtered and mapped elements with Maybe", () => { 153 | fc.assert( 154 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 155 | const criteria = (item: number) => item < 3; 156 | const map = (item: number) => (item < 3 ? Maybe.just(item + 5) : Maybe.none()); 157 | const iterator: LazyIterator = LazyIterator.from(arr); 158 | expect([...iterator.filterMap(map)]).toEqual([ 159 | ...arr 160 | .filter(criteria) 161 | .map(map) 162 | .map(a => a.value) 163 | ]); 164 | }) 165 | ); 166 | }); 167 | it("should get right filtered and mapped elements", () => { 168 | fc.assert( 169 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 170 | const criteria = (item: number) => item < 3; 171 | const map = (item: number) => (item < 3 ? item + 5 : undefined); 172 | const iterator: LazyIterator = LazyIterator.from(arr); 173 | expect([...iterator.filterMap(map)]).toEqual([...arr.filter(criteria).map(map)]); 174 | }) 175 | ); 176 | }); 177 | }); 178 | describe("find", () => { 179 | it("should find element with Maybe", () => { 180 | fc.assert( 181 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 182 | const iterator: LazyIterator = LazyIterator.from(arr); 183 | const finder = (a: number) => a === 3; 184 | const element = iterator.find(finder); 185 | if (arr.find(finder)) { 186 | expect(element).toEqual(Maybe.just(3)); 187 | } else { 188 | expect(element).toEqual(Maybe.none()); 189 | } 190 | }) 191 | ); 192 | }); 193 | it("should find element", () => { 194 | fc.assert( 195 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 196 | const iterator: LazyIterator = LazyIterator.from(arr); 197 | const finder = (a: number) => a === 3; 198 | expect(iterator.find(finder, true)).toBe(arr.find(finder)); 199 | }) 200 | ); 201 | }); 202 | }); 203 | describe("findMap", () => { 204 | it("should find and map element with Maybe", () => { 205 | fc.assert( 206 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 207 | const iterator: LazyIterator = LazyIterator.from(arr); 208 | const element1 = iterator.findMap(a => (a === 3 ? Maybe.just(a + 3) : Maybe.none())); 209 | if (arr.includes(3)) { 210 | expect(element1).toEqual(Maybe.just(6)); 211 | } else { 212 | expect(element1).toEqual(Maybe.none()); 213 | } 214 | }) 215 | ); 216 | }); 217 | it("should find and map element", () => { 218 | fc.assert( 219 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 220 | const iterator: LazyIterator = LazyIterator.from(arr); 221 | const element1 = iterator.findMap(a => (a === 3 ? a + 3 : undefined), true); 222 | if (arr.find(a => a === 3)) { 223 | expect(element1).toEqual(6); 224 | } else { 225 | expect(element1).toEqual(undefined); 226 | } 227 | }) 228 | ); 229 | }); 230 | }); 231 | describe("flatMap", () => { 232 | it("should flatten and map elements", () => { 233 | fc.assert( 234 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 235 | const iterator: LazyIterator = LazyIterator.from(arr); 236 | const mapper = (a: number) => LazyIterator.from([a + 2]); 237 | const result = iterator.flatMap(mapper); 238 | const expected = arr.reduce((arr, el) => [...arr, ...mapper(el)], []); 239 | expect([...result]).toEqual(expected); 240 | }) 241 | ); 242 | }); 243 | }); 244 | describe("flatten", () => { 245 | it("should flatten elements", () => { 246 | fc.assert( 247 | fc.property(fc.array(fc.nat()), fc.array(fc.nat()), (arr1: number[], arr2: number[]) => { 248 | const iterator1: LazyIterator = LazyIterator.from(arr1); 249 | const iterator2: LazyIterator = LazyIterator.from(arr2); 250 | const iterator: LazyIterator> = LazyIterator.from([iterator1, iterator2]); 251 | const result = iterator.flatten(); 252 | const expected = [...arr1, ...arr2]; 253 | expect([...result]).toEqual(expected); 254 | }) 255 | ); 256 | }); 257 | }); 258 | describe("forEach", () => { 259 | it("should run fn for all elements", () => { 260 | fc.assert( 261 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 262 | const iterator: LazyIterator = LazyIterator.from(arr); 263 | const fn = jest.fn(el => el); 264 | iterator.forEach(fn); 265 | expect(fn.mock.calls.length).toBe(arr.length); 266 | fn.mock.calls.forEach(([el], i) => { 267 | expect(el).toBe(arr[i]); 268 | }); 269 | }) 270 | ); 271 | }); 272 | }); 273 | describe("last", () => { 274 | it("should get right last element with Maybe", () => { 275 | fc.assert( 276 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 277 | const iterator: LazyIterator = LazyIterator.from(arr); 278 | const last = arr[arr.length - 1]; 279 | expect(iterator.last()).toEqual(last === undefined ? Maybe.none() : Maybe.just(last)); 280 | }) 281 | ); 282 | }); 283 | it("should get right last element", () => { 284 | fc.assert( 285 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 286 | const iterator: LazyIterator = LazyIterator.from(arr); 287 | const last = arr[arr.length - 1]; 288 | expect(iterator.last(true)).toBe(last); 289 | }) 290 | ); 291 | }); 292 | }); 293 | describe("map", () => { 294 | it("should get mapped elements", () => { 295 | fc.assert( 296 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 297 | const iterator: LazyIterator = LazyIterator.from(arr); 298 | const mapper = (a: number) => a + 2; 299 | expect([...iterator.map(mapper)]).toEqual(arr.map(mapper)); 300 | }) 301 | ); 302 | }); 303 | }); 304 | describe("max", () => { 305 | it("should get right max element with maybe", () => { 306 | fc.assert( 307 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 308 | const iterator: LazyIterator = LazyIterator.from(arr); 309 | const max1 = iterator.max(); 310 | const max2 = Math.max(...arr); 311 | expect(max1).toEqual(isFinite(max2) ? Maybe.just(max2) : Maybe.none()); 312 | }) 313 | ); 314 | }); 315 | it("should get right max element", () => { 316 | fc.assert( 317 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 318 | const iterator: LazyIterator = LazyIterator.from(arr); 319 | const max1 = iterator.max(a => a, true); 320 | const max2 = Math.max(...arr); 321 | expect(max1).toEqual(isFinite(max2) ? max2 : undefined); 322 | }) 323 | ); 324 | }); 325 | }); 326 | describe("min", () => { 327 | it("should get right min element with maybe", () => { 328 | fc.assert( 329 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 330 | const iterator: LazyIterator = LazyIterator.from(arr); 331 | const min1 = iterator.min(); 332 | const min2 = Math.min(...arr); 333 | expect(min1).toEqual(isFinite(min2) ? Maybe.just(min2) : Maybe.none()); 334 | }) 335 | ); 336 | }); 337 | it("should get right min element", () => { 338 | fc.assert( 339 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 340 | const iterator: LazyIterator = LazyIterator.from(arr); 341 | const min1 = iterator.min(a => a, true); 342 | const min2 = Math.min(...arr); 343 | expect(min1).toEqual(isFinite(min2) ? min2 : undefined); 344 | }) 345 | ); 346 | }); 347 | }); 348 | describe("nth", () => { 349 | it("should get right nth element with maybe", () => { 350 | fc.assert( 351 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], index: number) => { 352 | const iterator: LazyIterator = LazyIterator.from(arr); 353 | const nth1 = iterator.nth(index); 354 | const nth2 = arr[index]; 355 | expect(nth1).toEqual(nth2 !== undefined ? Maybe.just(nth2) : Maybe.none()); 356 | }) 357 | ); 358 | }); 359 | it("should get right nth element", () => { 360 | fc.assert( 361 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], index: number) => { 362 | const iterator: LazyIterator = LazyIterator.from(arr); 363 | const nth1 = iterator.nth(index, true); 364 | const nth2 = arr[index]; 365 | expect(nth1).toBe(nth2); 366 | }) 367 | ); 368 | }); 369 | }); 370 | describe("partion", () => { 371 | it("should get right partioned elements", () => { 372 | fc.assert( 373 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 374 | const iterator: LazyIterator = LazyIterator.from(arr); 375 | const filterFn = (a: number) => a < 3; 376 | const [left1, right1] = iterator.partion(filterFn); 377 | const [left2, right2] = [arr.filter(filterFn), arr.filter(a => !filterFn(a))]; 378 | expect(left1).toEqual(left2); 379 | expect(right1).toEqual(right2); 380 | }) 381 | ); 382 | }); 383 | }); 384 | describe("position", () => { 385 | it("should get right position of element with maybe", () => { 386 | fc.assert( 387 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 388 | const iterator: LazyIterator = LazyIterator.from(arr); 389 | const position1 = iterator.position(a => a === 3); 390 | const position2 = arr.indexOf(3); 391 | expect(position1).toEqual(position2 === -1 ? Maybe.none() : Maybe.just(position2)); 392 | }) 393 | ); 394 | }); 395 | it("should get right position of element", () => { 396 | fc.assert( 397 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 398 | const iterator: LazyIterator = LazyIterator.from(arr); 399 | const position1 = iterator.position(a => a === 3, true); 400 | const position2 = arr.indexOf(3); 401 | expect(position1).toBe(position2 === -1 ? undefined : position2); 402 | }) 403 | ); 404 | }); 405 | }); 406 | describe("product", () => { 407 | it("should get right product of elements", () => { 408 | fc.assert( 409 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 410 | const iterator: LazyIterator = LazyIterator.from(arr); 411 | const product1 = iterator.product(); 412 | const product2 = arr.reduce((p, e) => p * e, 1); 413 | expect(product1).toBe(product2); 414 | }) 415 | ); 416 | }); 417 | }); 418 | describe("reverse", () => { 419 | it("should get right reversed elements", () => { 420 | fc.assert( 421 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 422 | const iterator: LazyIterator = LazyIterator.from(arr); 423 | expect([...iterator.reverse()]).toEqual(arr.reverse()); 424 | }) 425 | ); 426 | }); 427 | }); 428 | describe("scan", () => { 429 | it("should get right scanned elements", () => { 430 | fc.assert( 431 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 432 | const iterator: LazyIterator = LazyIterator.from(arr); 433 | const products1 = [...iterator.scan((a, b) => a * b, 1)]; 434 | const products2 = arr.reduce<[number, number[]]>(([a, els], b) => [a * b, els.concat([a * b])], [1, []]); 435 | expect(products1).toEqual(products2[1]); 436 | }) 437 | ); 438 | }); 439 | }); 440 | describe("skip", () => { 441 | it("should get right skipped elements", () => { 442 | fc.assert( 443 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 444 | const iterator: LazyIterator = LazyIterator.from(arr); 445 | expect([...iterator.skip(2)]).toEqual(arr.slice(2)); 446 | }) 447 | ); 448 | }); 449 | }); 450 | describe("skipWhile", () => { 451 | it("should get right skipped elements", () => { 452 | fc.assert( 453 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 454 | const iterator: LazyIterator = LazyIterator.from(arr); 455 | const index = arr.indexOf(3); 456 | expect([...iterator.skipWhile(a => a !== 3)]).toEqual(index === -1 ? [] : arr.slice(index)); 457 | }) 458 | ); 459 | }); 460 | }); 461 | describe("stepBy", () => { 462 | it("should get right stepped elements", () => { 463 | fc.assert( 464 | fc.property( 465 | fc.array(fc.nat()), 466 | fc.nat().filter((a: number) => a > 0), 467 | (arr: number[], step: number) => { 468 | const iterator: LazyIterator = LazyIterator.from(arr); 469 | expect([...iterator.stepBy(step)]).toEqual(arr.filter((_, i) => i % step === 0)); 470 | } 471 | ) 472 | ); 473 | }); 474 | }); 475 | describe("sum", () => { 476 | it("should get right sum of elements", () => { 477 | fc.assert( 478 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 479 | const iterator: LazyIterator = LazyIterator.from(arr); 480 | const sum1 = iterator.sum(); 481 | const sum2 = arr.reduce((a, b) => a + b, 0); 482 | expect(sum1).toBe(sum2); 483 | }) 484 | ); 485 | }); 486 | }); 487 | describe("take", () => { 488 | it("should take right elements", () => { 489 | fc.assert( 490 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], count: number) => { 491 | const iterator: LazyIterator = LazyIterator.from(arr); 492 | expect([...iterator.take(count)]).toEqual(arr.slice(0, count)); 493 | }) 494 | ); 495 | }); 496 | }); 497 | describe("takeWhile", () => { 498 | it("should take right elements", () => { 499 | fc.assert( 500 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 501 | const iterator: LazyIterator = LazyIterator.from(arr); 502 | const index = arr.findIndex(a => a >= 3); 503 | expect([...iterator.takeWhile(a => a < 3)]).toEqual(index === -1 ? arr : arr.slice(0, index)); 504 | }) 505 | ); 506 | }); 507 | }); 508 | describe("unzip", () => { 509 | it("should get right unzipped elements", () => { 510 | fc.assert( 511 | fc.property(fc.array(fc.tuple(fc.nat(), fc.nat())), (arr: Array<[number, number]>) => { 512 | const iterator: LazyIterator<[number, number]> = LazyIterator.from(arr); 513 | const [left, right] = iterator.unzip(); 514 | for (let i = 0; i < arr.length; i++) { 515 | const [l, r] = arr[i]; 516 | expect(left[i]).toBe(l); 517 | expect(right[i]).toBe(r); 518 | } 519 | }) 520 | ); 521 | }); 522 | }); 523 | describe("zip", () => { 524 | it("should get right zipped elements", () => { 525 | fc.assert( 526 | fc.property(fc.array(fc.nat()), fc.array(fc.nat()), (arr1: number[], arr2: number[]) => { 527 | const iterator: LazyIterator = LazyIterator.from(arr1); 528 | const zipped1 = iterator.zip(LazyIterator.from(arr2)); 529 | const zipped2 = arr1.map((el, i) => [el, arr2[i]]).filter(a => a.every(a => a !== undefined)); 530 | expect([...zipped1]).toEqual(zipped2); 531 | }) 532 | ); 533 | }); 534 | }); 535 | describe("compress", () => { 536 | it("should get right compressed elements", () => { 537 | fc.assert( 538 | fc.property(fc.array(fc.nat()), fc.array(fc.boolean()), (arr: number[], bits: Array) => { 539 | const iterator: LazyIterator = LazyIterator.from(arr); 540 | const compressed1 = iterator.compress(bits); 541 | const compressed2 = arr.filter((_, i) => bits[i]); 542 | expect([...compressed1]).toEqual(compressed2); 543 | }) 544 | ); 545 | }); 546 | }); 547 | describe("permutations", () => { 548 | it("should get right permutated elements", () => { 549 | fc.assert( 550 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 551 | const iterator: LazyIterator = LazyIterator.from(arr); 552 | const permutated1 = iterator.permutations(); 553 | const permutated2 = arr.reduce( 554 | (res: [number, number][], el1, i1) => res.concat(arr.filter((_, i2) => i1 !== i2).map(el2 => [el1, el2])), 555 | [] as Array<[number, number]> 556 | ); 557 | expect([...permutated1]).toEqual(permutated2); 558 | }) 559 | ); 560 | }); 561 | }); 562 | describe("slice", () => { 563 | it("should get right sliced elements", () => { 564 | fc.assert( 565 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 566 | const iterator: LazyIterator = LazyIterator.from(arr); 567 | const sliced1 = iterator.slice(2, 6); 568 | const sliced2 = arr.slice(2, 6); 569 | expect([...sliced1]).toEqual(sliced2); 570 | }) 571 | ); 572 | }); 573 | }); 574 | describe("compact", () => { 575 | it("should get right compacted elements", () => { 576 | fc.assert( 577 | fc.property(fc.array(fc.oneof(fc.nat(), fc.constant(undefined))), (arr: Array) => { 578 | const iterator: LazyIterator = LazyIterator.from(arr); 579 | const compacted1 = iterator.compact(); 580 | const compacted2 = arr.filter(a => a !== undefined); 581 | expect([...compacted1]).toEqual(compacted2); 582 | }) 583 | ); 584 | }); 585 | }); 586 | describe("contains", () => { 587 | it("should show including element in iterator", () => { 588 | fc.assert( 589 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], element: number) => { 590 | const iterator: LazyIterator = LazyIterator.from(arr); 591 | expect(iterator.contains(element)).toEqual(arr.includes(element)); 592 | }) 593 | ); 594 | }); 595 | }); 596 | describe("unique", () => { 597 | it("should get right unique elements", () => { 598 | fc.assert( 599 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 600 | const iterator: LazyIterator = LazyIterator.from(arr); 601 | const unique1 = iterator.unique(); 602 | const unique2 = arr.reduce((res, el) => (res.includes(el) ? res : [...res, el]), [] as number[]); 603 | expect([...unique1]).toEqual(unique2); 604 | }) 605 | ); 606 | }); 607 | }); 608 | describe("except", () => { 609 | it("should get right excepted elements", () => { 610 | fc.assert( 611 | fc.property(fc.array(fc.nat()), fc.array(fc.nat()), (arr1: number[], arr2: number[]) => { 612 | const iterator1: LazyIterator = LazyIterator.from(arr1); 613 | const iterator2: LazyIterator = LazyIterator.from(arr2); 614 | const except1 = iterator1.except(iterator2); 615 | const except2 = arr1.filter(a => !arr2.includes(a)); 616 | expect([...except1]).toEqual(except2); 617 | }) 618 | ); 619 | }); 620 | }); 621 | describe("intersect", () => { 622 | it("should get right intersected elements", () => { 623 | fc.assert( 624 | fc.property(fc.array(fc.nat()), fc.array(fc.nat()), (arr1: number[], arr2: number[]) => { 625 | const iterator1: LazyIterator = LazyIterator.from(arr1); 626 | const iterator2: LazyIterator = LazyIterator.from(arr2); 627 | const except1 = iterator1.intersect(iterator2); 628 | const except2 = arr1.filter(a => arr2.includes(a)); 629 | expect([...except1]).toEqual(except2); 630 | }) 631 | ); 632 | }); 633 | }); 634 | describe("isEmpty", () => { 635 | it("should show right empty iterator", () => { 636 | fc.assert( 637 | fc.property(fc.array(fc.nat()), (arr: number[]) => { 638 | const iterator: LazyIterator = LazyIterator.from(arr); 639 | expect(iterator.isEmpty()).toBe(arr.length === 0); 640 | }) 641 | ); 642 | }); 643 | }); 644 | describe("prepend", () => { 645 | it("should add right element to head of iterator", () => { 646 | fc.assert( 647 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], element: number) => { 648 | const iterator: LazyIterator = LazyIterator.from(arr); 649 | const newIterator: LazyIterator = iterator.prepend(element); 650 | expect([...newIterator]).toEqual([element, ...arr]); 651 | }) 652 | ); 653 | }); 654 | }); 655 | describe("append", () => { 656 | it("should add right element to tail of iterator", () => { 657 | fc.assert( 658 | fc.property(fc.array(fc.nat()), fc.nat(), (arr: number[], element: number) => { 659 | const iterator: LazyIterator = LazyIterator.from(arr); 660 | const newIterator: LazyIterator = iterator.append(element); 661 | expect([...newIterator]).toEqual([...arr, element]); 662 | }) 663 | ); 664 | }); 665 | }); 666 | }); 667 | -------------------------------------------------------------------------------- /tests/maybe.test.ts: -------------------------------------------------------------------------------- 1 | import * as fc from "fast-check"; 2 | import { fromNullable, just, merge, none } from "@sweet-monads/maybe"; 3 | import { left, right } from "../either"; 4 | 5 | describe("Maybe", () => { 6 | test("merge", () => 7 | fc.assert( 8 | fc.property(fc.subarray(["1", "2", "3"]), fc.subarray(["4", "5", "6"]), (noneItems, justItems) => { 9 | const mergedOne = merge([...noneItems.map(() => none()), ...justItems.map(y => just(y))]); 10 | expect(mergedOne.isNone()).toBe(noneItems.length > 0); 11 | expect(mergedOne.isJust()).toBe(noneItems.length === 0); 12 | 13 | const mergedTwo = merge([...justItems.map(y => just(y)), ...noneItems.map(() => none())]); 14 | expect(mergedTwo.isNone()).toBe(noneItems.length > 0); 15 | expect(mergedTwo.isJust()).toBe(noneItems.length === 0); 16 | }) 17 | )); 18 | 19 | test("fromNullable", () => 20 | fc.assert( 21 | fc.property(fc.option(fc.string()), option => { 22 | const nil = fromNullable(option); 23 | expect(nil.isJust()).toBe(option !== null && option !== undefined); 24 | expect(nil.isNone()).toBe(option === null || option === undefined); 25 | }) 26 | )); 27 | 28 | test("unwrap", () => { 29 | const v1 = just(2); 30 | const v2 = none(); 31 | 32 | expect(v1.unwrap()).toBe(2); 33 | expect(() => v2.unwrap()).toThrow(new Error("Value is None")); 34 | 35 | const customError = new ReferenceError("Custom error message"); 36 | 37 | expect(v1.unwrap(() => customError)).toBe(2); 38 | expect(() => v2.unwrap(() => customError)).toThrow(customError); 39 | }); 40 | 41 | test("unwrapOr", () => { 42 | const v1 = just(2); 43 | const v2 = none(); 44 | 45 | expect(v1.unwrapOr(3)).toBe(2); 46 | expect(v2.unwrapOr(3)).toBe(3); 47 | }); 48 | 49 | test("unwrapOrElse", () => { 50 | const v1 = just(2); 51 | const v2 = none(); 52 | 53 | expect(v1.unwrapOrElse(() => 6)).toBe(2); 54 | expect(v2.unwrapOrElse(() => 6)).toBe(6); 55 | }); 56 | 57 | test("fold", () => { 58 | const mapNone = (): string | number => { 59 | return "none"; 60 | }; 61 | const mapJust = (value: number): string | number => { 62 | return value * 4; 63 | }; 64 | expect(just(2).fold(mapNone, mapJust)).toBe(8); 65 | expect(none().fold(mapNone, mapJust)).toBe("none"); 66 | }); 67 | 68 | test("mapNullable", () => { 69 | const v1 = just(2); 70 | const v2 = none(); 71 | 72 | expect(v1.mapNullable(a => a.toString()).unwrap()).toBe("2"); 73 | expect(v2.mapNullable(a => a.toString())).toBe(none()); 74 | expect(v2.mapNullable(a => null)).toBe(none()); 75 | expect(v2.mapNullable(a => undefined)).toBe(none()); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2018"], 4 | "target": "es2015", 5 | "declaration": true, 6 | "noImplicitAny": true, 7 | "removeComments": true, 8 | "strictNullChecks": true, 9 | "preserveConstEnums": true, 10 | "experimentalDecorators": true, 11 | "downlevelIteration": true, 12 | "moduleResolution": "Node" 13 | }, 14 | "include": ["./index.ts"], 15 | "exclude": ["node_modules"] 16 | } 17 | --------------------------------------------------------------------------------