Skip to content

Commit

Permalink
Big rewriting and simplifying the library.
Browse files Browse the repository at this point in the history
After using this library in production I've found that many of functions and structures are unuseful.

%%% Added

- `Reducer` and `Transducer` types.
- `reducer` function to build reducer function.
- `transduce` function to transform foldable instance into some value.
- `map` and `filter` functions that build transducer functions.
- `toArray` and `toList` reducer builders.

%%% Changed

- `take` and `skip` methods of `List` accept a _while_ predicate function now.
- `reduce` method of `List` to accept a `Reducer` only.
- `prepend` method of `List` accepts another `List` only.
- `send` method of `Stream` returns `void`.
- `tap` can accept asynchronous effect.

%%% Fixed

- pass _deep_ parameter to recursive call of `freeze` function.

%%% Removed

- `Compressable` type.
- `compress` method from `List`.
- `uniqueBy` method from `List` because it hides cache implementation.
- `append` method of `List`.
- `StreamEvents` enum.
- `on`, `freeze`, `resume`, `destroy`, `compress` and `uniqueBy` methods from `Stream`.
- `Container`, `Lazy` and `Tuple` monads.
  • Loading branch information
Kapelianovych committed Sep 4, 2021
1 parent 6252da7 commit 7d8f9c8
Show file tree
Hide file tree
Showing 29 changed files with 406 additions and 773 deletions.
34 changes: 33 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
# [0.33.0] - 2021-09-02
# [0.34.0] - 2021-09-04

### Added

- `Reducer` and `Transducer` types.
- `reducer` function to build reducer function.
- `transduce` function to transform foldable instance into some value.
- `map` and `filter` functions that build transducer functions.
- `toArray` and `toList` reducer builders.

### Changed

- `take` and `skip` methods of `List` accept a _while_ predicate function now.
- `reduce` method of `List` to accept a `Reducer` only.
- `prepend` method of `List` accepts another `List` only.
- `send` method of `Stream` returns `void`.
- `tap` can accept asynchronous effect.

### Fixed

- pass _deep_ parameter to recursive call of `freeze` function.

### Removed

- `Compressable` type.
- `compress` method from `List`.
- `uniqueBy` method from `List` because it hides cache implementation.
- `append` method of `List`.
- `StreamEvents` enum.
- `on`, `freeze`, `resume`, `destroy`, `compress` and `uniqueBy` methods from `Stream`.
- `Container`, `Lazy` and `Tuple` monads.

## [0.33.0] - 2021-09-02

### Added

Expand Down
134 changes: 43 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,45 @@ const result3 = memoizedFn(4); // Function is executed
memoizedFn.cache.clear();
```
### transduce
```ts
function transduce<T extends Foldable<any>>(
instance: T,
): <I, K>(
aggregator: Reducer<I, K>,
) => <R extends ReadonlyArray<Transducer<I, any, any>>>(
...transducers: ChainTransducers<R>
) => I;
```
Creates transduce operation over a `Foldable` instance.
```ts
const result /*: readonly string[] */ = transduce([1, 2, 3])(toArray<string>())(
filter<ReadonlyArray<string>, number>((value) => value >= 2),
map<ReadonlyArray<string>, number, string>(String),
);
```
There are two functions that builds transducers: `map` and `filter`.
### reducer
```ts
function reducer<T>(
initial: T,
): <K>(fn: (accumulator: T, current: K) => T) => Reducer<T, K>;
```
Helps building reducer.
```ts
const reduceFunction /*: Reducer<number, number> */ = reducer(0)(binary('+')); // create reducer that sum numbers.
```
There are two predefined reducers that collect value: `toArray` and `toList`.
### array
```typescript
Expand Down Expand Up @@ -524,7 +563,7 @@ const getUser /*: (id: string) => User */ = tryCatch(
### tap
```ts
function tap<T>(effect: (value: T) => void): (value: T) => T;
function tap<T>(effect: (value: T) => void | Promise<void>): (value: T) => T;
```
Performs side effect on value while returning it as is.
Expand Down Expand Up @@ -584,49 +623,6 @@ const result /*: number */ = multiplyIf(9); // Will be returned as is.
const result2 /*: number */ = multiplyIf(11); // Will be multiplied.
```
### wrap
```typescript
function wrap<T>(value: T): Container<T>;
```
Wraps value in `Container` monad and allow perform on it operations in chainable way.
```typescript
wrap(1)
.map((num) => num + '0')
.chain((str) => wrap(parseInt(str)))
.apply(wrap((num) => Math.pow(num, 2)))
.extract(); // => 100
```
### isContainer
```typescript
function isContainer<T>(value: unknown): value is Container<T>;
```
Check if value is instance of Container.
```typescript
isContainer(wrap(1)); // true
isContainer(1); // false
```
#### Container
Monad that contains value and allow perform operation on it by set of methods.
1. `map<R>(fn: (value: T) => R): Container<R>` - maps inner value and returns new `Container` instance with new value.
2. `chain<R>(fn: (value: T) => Container<R>): Container<R>` - the same as `map`, but function must return already wrapped value.
3. `apply<R>(other: Container<(value: T) => R>): Container<R>` - maps value by using value of `other` wrapper. Value of other wrapper must be a function type.
4. `extract(): T` - expose inner value to outside.
> These methods have also `Option` and `Either` monads.
### isOption
```typescript
Expand Down Expand Up @@ -882,43 +878,6 @@ const result /*: boolean */ = isList(list());
Monad that represents lazy `Array`. It can decrease computation step comparably to `Array`. Actual execution of `List`'s methods starts when one of _terminating method_ (method that do not return List instance) is called.
### lazy
```typescript
function lazy<F, L>(value: Operation<F, L> | Lazy<F, L>): Lazy<F, L>;
```
Creates `Lazy` monad with some operation or from another `Lazy` instance.
```typescript
const lazyPower /*: Lazy<number, number> */ = lazy((num: number) =>
Math.pow(num, 2),
);
```
#### Lazy
Monad that constructs and compose operations over some value. Similar to `pipe` function, but allows more comprehensive transformation of intermediate values.
### tuple
```typescript
function tuple<T extends ReadonlyArray<unknown>>(...args: T): Tuple<T>;
```
Creates `Tuple` from set of elements.
```typescript
const y /*: Tuple<[number, string]> */ = tuple(9, 'state');

// Tuple can be destructured
const [num, str] = y;
```
#### Tuple
Immutable container for fixed sequence of values.
### stream
```typescript
Expand All @@ -935,7 +894,7 @@ y.map((value) => Math.pow(value, 2)).listen(
);

// Somewhere in the code
y.send(2); // document.body.innerHTML will be equal to 4
y.send(2); // document.body.innerHTML will set to equal to 4
```
#### Stream
Expand Down Expand Up @@ -966,17 +925,10 @@ Monad that allow to defer data initialization.
function reviver(
key: string,
value: JSONValueTypes | SerializabledObject<any>,
):
| JSONValueTypes
| List<any>
| Idle<any>
| Option<any>
| Container<any>
| Either<Error, any>
| Tuple<ReadonlyArray<any>>;
): JSONValueTypes | List<any> | Idle<any> | Option<any> | Either<Error, any>;
```
Add recognition of `Container`, `Idle`, `Tuple`, `Option`, `List`, `Either` data structures for `JSON.parse`.
Add recognition of `Idle`, `Option`, `List`, `Either` data structures for `JSON.parse`.
```typescript
const obj = JSON.parse('{"type":"Some","value":1}', reviver);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fluss/core",
"version": "0.33.0",
"version": "0.34.0",
"description": "Core functions and structures for functional programming.",
"keywords": [
"functional-programming",
Expand Down
18 changes: 6 additions & 12 deletions src/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ import { isObject } from './is_object';
/** Creates readonly array from set of ArrayLike, Iterable objects or values. */
export const array = <T>(
...values: ReadonlyArray<T | ArrayLike<T> | Iterable<T>>
): ReadonlyArray<T> => {
// Here must be freezing array operation,
// but due to [this Chromium bug](https://bugs.chromium.org/p/chromium/issues/detail?id=980227)
// it is very slow operation and this action is not performed.
return values
.map((value) =>
isObject(value) && ('length' in value || Symbol.iterator in value)
? Array.from(value as ArrayLike<T> | Iterable<T>)
: Array.of(value as T)
)
.reduce((accumulator, current) => accumulator.concat(current), []);
};
): ReadonlyArray<T> =>
values.flatMap((value) =>
isObject(value) && ('length' in value || Symbol.iterator in value)
? Array.from(value as ArrayLike<T> | Iterable<T>)
: Array.of(value as T),
);
35 changes: 0 additions & 35 deletions src/container.ts

This file was deleted.

44 changes: 26 additions & 18 deletions src/either.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { isObject } from './is_object';
import { isFunction } from './is_function';
import type { Typeable, Serializable } from './types';
import type { Typeable, Serializable, Monad, Comonad } from './types';

export const EITHER_LEFT_OBJECT_TYPE = '$Left';
export const EITHER_RIGHT_OBJECT_TYPE = '$Right';

export interface Right<B> extends Typeable, Serializable<B> {
map<R>(fn: (value: B) => R): Right<R>;
chain<E extends Either<any, any>>(fn: (value: B) => E): E;
apply<R>(other: Right<(value: B) => R>): Right<R>;
handle(): Right<B>;
isLeft(): this is Right<never>;
isRight(): this is Right<B>;
extract(): B;
export interface Right<B>
extends Typeable,
Monad<B>,
Comonad<B>,
Serializable<B> {
readonly map: <R>(fn: (value: B) => R) => Right<R>;
readonly chain: <E extends Either<any, any>>(fn: (value: B) => E) => E;
readonly apply: <R>(other: Right<(value: B) => R>) => Right<R>;
readonly handle: () => Right<B>;
readonly isLeft: () => this is Right<never>;
readonly isRight: () => this is Right<B>;
readonly extract: () => B;
}

export interface Left<A> extends Typeable, Serializable<A> {
map(): Left<A>;
chain(): Left<A>;
apply(): Left<A>;
isLeft(): this is Left<A>;
isRight(): this is Left<never>;
extract(): A;
handle<B>(fn: (value: A) => B): Right<B>;
export interface Left<A>
extends Typeable,
Monad<A>,
Comonad<A>,
Serializable<A> {
readonly map: () => Left<A>;
readonly chain: () => Left<A>;
readonly apply: () => Left<A>;
readonly isLeft: () => this is Left<A>;
readonly handle: <B>(fn: (value: A) => B) => Right<B>;
readonly isRight: () => this is Left<never>;
readonly extract: () => A;
}

/**
Expand Down Expand Up @@ -68,7 +76,7 @@ export const left = <A>(value: A): Left<A> => ({
*/
export const either = <A, B>(
isRight: (value: A | B) => value is B,
value: A | B
value: A | B,
): Either<A, B> => (isRight(value) ? right(value) : left(value));

/** Checks if value is instance of `Either` monad. */
Expand Down
8 changes: 5 additions & 3 deletions src/freeze.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isObject } from './is_object';
import { isFunction } from './is_function';
import type { DeepReadonly } from './utilities';

/**
Expand All @@ -7,13 +9,13 @@ import type { DeepReadonly } from './utilities';
export const freeze = <T extends object, D extends boolean = false>(
value: T,
// @ts-ignore - TS cannot assign false to boolean :(
deep: D = false
deep: D = false,
): D extends true ? DeepReadonly<T> : Readonly<T> => {
if (deep) {
Object.getOwnPropertyNames(value).forEach((name) => {
const innerValue = (value as { [key: string]: any })[name];
if (typeof innerValue === 'object' || typeof innerValue === 'function') {
freeze(innerValue);
if (isObject(innerValue) || isFunction(innerValue)) {
freeze(innerValue, deep);
}
});
}
Expand Down

0 comments on commit 7d8f9c8

Please sign in to comment.