Commit 4685aafc by Vladislav Lagunov

Merge branch 'master' of ssh://git.bitmaster.ru:34022/npm/common

parents d67d4f07 cbaa6040
......@@ -17,6 +17,8 @@ export type Eff<Error, Success> =
| Apply<Error, Success> // { args: Eff<unknown>[], proj(...args): Either<Error, Success> }
| Chain<Error, Success> // { first: Eff<unknown>, andThen(x: unknown): Eff<Error, Success> }
| HasEffect<Error, Success> // { toEffect(): Eff<Error, Success> }
;
export type AnyEff = Eff<any, any>;
// Instance methods for `Eff`
......@@ -140,7 +142,7 @@ export function ap(): Eff<unknown, unknown> {
}
export function record<R extends Record<string, Eff<any, any>>>(rec: R): Eff<{ [K in keyof R]: R[K]['_Error'] }[keyof R], { [K in keyof R]: R[K]['_Success'] }> {
export function record<R extends Record<string, AnyEff>>(rec: R): Eff<{ [K in keyof R]: R[K]['_Error'] }[keyof R], { [K in keyof R]: R[K]['_Success'] }> {
const keys = Object.keys(rec);
return ap.apply(undefined, [...keys.map(k => rec[k]), (...values) => values.reduce((acc, v, idx) => (acc[keys[idx]] = v, acc), {})]);
}
......@@ -240,23 +242,27 @@ export function go<Error, Success>(effect: Eff<Error, Success>, onNext: (x: Eith
}
if (effect instanceof Chain) {
const subscriptions: Array<Function|null> = [];
subscriptions.push(go(effect.first, result => {
const idx = subscriptions.length;
subscriptions.push(go(effect.andThen(result), onNext, () => {
subscriptions[idx] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
onComplete();
}));
}, () => {
subscriptions[0] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
onComplete();
}));
const cancellers = new Map<AnyEff, Canceller>();
const handleEffect = (e: AnyEff) => {
const _onNext = result => {
if (e === effect.first) handleEffect(effect.andThen(result));
else onNext(result);
};
const _onComplete = () => {
if (!cancellers.has(e)) completedImmediately = true;
cancellers.delete(e);
if (cancellers.size === 0) onComplete();
};
let completedImmediately = false;
const canceller = go(e, _onNext, _onComplete);
if (!completedImmediately) cancellers.set(e, canceller);
};
handleEffect(effect.first);
if (cancellers.size === 0) return noopFunc;
return () => subscriptions.forEach(
funOrNull => funOrNull ? funOrNull() : void 0
);
return () => cancellers.forEach(canceller => canceller());
}
if (effect instanceof Apply) {
......@@ -355,6 +361,9 @@ export abstract class HasEffect<Error, Success> extends EffBase<Error, Success>
export const noop: Cmd<never> = new Batch([]);
export type Canceller = () => void;
export interface EffStatics {
of: typeof of,
success: typeof success,
......
......@@ -12,10 +12,10 @@ Update.Do = Do;
declare module "./" {
interface UpdateStatic {
Do<State, Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>;
Do<State, Result>(iter: () => IterableIterator<Update<Error, any, any>>): Update<Error, State, Result>;
}
interface BoundStatics<State> {
Do<Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>;
interface BoundStatics<Error, State> {
Do<Result>(iter: () => IterableIterator<Update<Error, any, any>>): Update<Error, State, Result>;
}
}
......
......@@ -6,6 +6,8 @@ import * as http from '../../http';
import { InputHTMLAttributes } from 'react';
type Err = http.HttpError;
type Props = {
};
......@@ -15,10 +17,10 @@ type State = {
response?: http.Response;
};
const U = Update.bind<State>();
const U = Update.bind<Err, State>();
class Widget extends Update.Component<Props, State> {
class Widget extends Update.Component<Err, Props, State> {
state: State = { search: '', pending: false };
handleNameChange: InputHTMLAttributes<HTMLInputElement>['onChange'] = (e) => this.setState({ search: e.target.value });
......
......@@ -5,23 +5,32 @@ import { absurd } from '../types';
// ADT
export type Update<State, A> =
| Pure<State, A> // { value: A }
| Modify<State, A> // { proj(x: State): State } // A ~ void
| Patch<State, A> // { patch: Partial<State> } // A ~ void
| Get<State, A> // { tag: 'Get' } // A ~ State
| Children<State, A> // { key: ControlledComponent<any, any>, update: Update<any, A> }
| Effect<State, A> // { effect: Eff<never, A> }
| FromPromise<State, A> // { promise: Promise<A> }
| Chain<State, A> // { update: Update<State, any>, chain(x: any): Update<State, A> }
| Apply<State, A> // { args: Update<State, any>[], proj(...args): A }
| Batch<State, A> // { steps: Updte<State, any>[] }
| Concat<State, A> // { steps: Updte<State, any>[] }
export type Update<Error, State, A> =
| Pure<Error, State, A> // { value: A }
| Modify<Error, State, A> // { proj(x: State): State } // A ~ void
| Patch<Error, State, A> // { patch: Partial<State> } // A ~ void
| Get<Error, State, A> // { tag: 'Get' } // A ~ State
| Children<Error, State, A> // { component: React.Component<any, any>, update: Update<any, A> }
| Effect<Error, State, A> // { effect: Eff<never, A> }
| FromPromise<Error, State, A> // { promise: Promise<A> }
| Chain<Error, State, A> // { update: Update<Error, State, any>, chain(x: any): Update<Error, State, A> }
| Apply<Error, State, A> // { args: Update<Error, State, any>[], proj(...args): A }
| Batch<Error, State, A> // { steps: Updte<Error, State, any>[] }
| Concat<Error, State, A> // { steps: Updte<Error, State, any>[] }
| Difference<Error, State, A> // { update: Update<A>, difference(prev: State, next: State): State|Update<Error, State, void> }
;
export type AnyUpdate = Update<any, any, any>;
// Perform state update
export function go<State, A>(update: Update<State, A>, component: React.Component<any, State>, onNext: (x: A) => void, onComplete: () => void): () => void {
export function go<Error, State, A>(
update: Update<Error, State, A>,
getState: () => State,
setState: SetState<State>,
onNext: (x: A) => void,
onError: (x: Error) => void,
onComplete: () => void
): Canceller {
if (update instanceof Pure) {
onNext(update.value);
onComplete();
......@@ -29,7 +38,7 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
}
if (update instanceof Modify) {
component['updater'].enqueueSetState(component, update.proj, () => {
setState(update.proj, () => {
onNext(void 0 as any);
update.callback && update.callback();
onComplete();
......@@ -38,7 +47,7 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
}
if (update instanceof Patch) {
component['updater'].enqueueSetState(component, update.patch, () => {
setState(update.patch, () => {
onNext(void 0 as any);
update.callback && update.callback();
onComplete();
......@@ -47,17 +56,19 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
}
if (update instanceof Get) {
onNext(component.state as any);
onNext(getState() as any as A);
onComplete();
return noopFunc;
}
if (update instanceof Children) {
return go(update.update, update.component, onNext, onComplete);
const _getState = () => update.component.state;
const _setState: SetState<any> = (projOrPatch, cb?) => update.component['updater'].enqueueSetState(update.component, projOrPatch, cb);
return go(update.update, _getState, _setState, onNext, onError, onComplete);
}
if (update instanceof Effect) {
return update.effect.run(onNext as any, onComplete);
return update.effect.run(ethr => ethr.fold(onError, onNext), onComplete);
}
if (update instanceof FromPromise) {
......@@ -66,28 +77,32 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
}
if (update instanceof Chain) {
const subscriptions: Array<Function|null> = [];
subscriptions.push(go(update.update, component, result => {
const idx = subscriptions.length;
subscriptions.push(go(update.andThen(result), component, onNext, () => {
subscriptions[idx] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
onComplete();
}));
}, () => {
subscriptions[0] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
onComplete();
}));
const cancellers = new Map<AnyUpdate, Canceller>();
const handleUpdate = (u: AnyUpdate) => {
const _onNext = result => {
if (u === update.update) handleUpdate(update.andThen(result));
else onNext(result);
};
const _onComplete = () => {
if (!cancellers.has(u)) completedImmediately = true;
cancellers.delete(u);
if (cancellers.size === 0) onComplete();
};
let completedImmediately = false;
const canceller = go(u, getState, setState as any, _onNext, onError, _onComplete);
if (!completedImmediately) cancellers.set(u, canceller);
};
handleUpdate(update.update);
if (cancellers.size === 0) return noopFunc;
return () => subscriptions.forEach(
funOrNull => funOrNull ? funOrNull() : void 0
);
return () => cancellers.forEach(canceller => canceller());
}
if (update instanceof Apply) {
let allInitialized = false;
let subscriptions: Array<Function|undefined|null> = new Array(update.args.length);
let cancellers: Array<Canceller|undefined|null> = new Array(update.args.length);
const initializedFlags: Array<true|undefined> = new Array(update.args.length);
const recentValues: unknown[] = new Array(update.args.length);
const next = idx => result => {
......@@ -101,46 +116,79 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
onNext(update.proj.apply(void 0, recentValues));
};
const complete = idx => () => {
subscriptions[idx] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
cancellers[idx] = null;
for (const c of cancellers) if (c !== null) return;
onComplete();
};
update.args.forEach((u, idx) => {
const canceller = go(u, component, next(idx), complete(idx));
if (subscriptions[idx] !== null) subscriptions[idx] = canceller;
const canceller = go(u, getState, setState, next(idx), onError, complete(idx));
if (cancellers[idx] !== null) cancellers[idx] = canceller;
});
return () => subscriptions.forEach(
funOrNull => funOrNull ? funOrNull() : void 0
);
return () => cancellers.forEach(canceller => canceller ? canceller() : void 0);
}
if (update instanceof Batch) {
if (update.steps.length === 0) { onNext(void 0 as any); onComplete(); return noopFunc; }
let subscriptions: Array<Function|null>;
let cancellers: Array<Canceller|null>;
const loop = idx => () => {
subscriptions[idx] = null;
for (const unsub of subscriptions) if (unsub !== null) return;
cancellers[idx] = null;
for (const unsub of cancellers) if (unsub !== null) return;
onNext(void 0 as any);
onComplete(); // If control flow reaches here, that means all nested commands are completed
};
subscriptions = update.steps.map((u, idx) => go(u, component, noopFunc, loop(idx)));
cancellers = update.steps.map((u, idx) => go(u, getState, setState, noopFunc, onError, loop(idx)));
return () => subscriptions.forEach(
funOrNull => funOrNull ? funOrNull() : void 0
);
return () => cancellers.forEach(canceller => canceller ? canceller() : void 0);
}
if (update instanceof Concat) {
let unsubscribe: Function|null = null;
let canceller: Canceller|null = null;
const loop = idx => () => {
// If condition holds, then all nested effects are completed, therefore we're done
if (idx >= update.steps.length) { onNext(void 0 as any); onComplete(); return; }
unsubscribe = go(update.steps[idx], component, noopFunc, loop(idx + 1));
canceller = go(update.steps[idx], getState, setState, noopFunc, onError, loop(idx + 1));
};
loop(0);
return () => unsubscribe ? unsubscribe() : void 0;
return () => canceller ? canceller() : void 0;
}
if (update instanceof Difference) {
const cancellers = new Map<AnyUpdate, Canceller>();
const _setState: SetState<State> = (patchOrProj, cb?) => {
const prev = getState();
// @ts-ignore
const next = typeof(patchOrProj) === 'function' ? patchOrProj(prev) : { ...prev, ...patchOrProj };
const stateOrUpdate = update.difference(prev, next);
if (stateOrUpdate instanceof UpdateBase) {
handleUpdate(stateOrUpdate);
} else {
setState(() => stateOrUpdate, cb);
}
};
const handleUpdate = (u: AnyUpdate) => {
const _onNext = result => {
if (u === update.update) onNext(result);
// Just ignore `void` result from spawned processes
};
const _onComplete = () => {
if (!cancellers.has(u)) completedImmediately = true;
cancellers.delete(u);
if (cancellers.size === 0) onComplete();
};
let completedImmediately = false;
const canceller = go(u, getState, u === update.update ? _setState : setState as any, _onNext, onError, _onComplete);
if (!completedImmediately) cancellers.set(u, canceller);
};
handleUpdate(update.update);
if (cancellers.size === 0) return noopFunc;
return () => cancellers.forEach(canceller => canceller());
}
return absurd(update);
......@@ -148,169 +196,191 @@ export function go<State, A>(update: Update<State, A>, component: React.Componen
// Instance methods
export class UpdateBase<State, A> {
export class UpdateBase<Error, State, A> {
readonly _Error: Error;
readonly _State: State;
readonly _A: A;
map<B>(proj: (x: A) => B): Apply<State, B> {
const self = this as any as Update<State, A>;
return new Apply<State, B>([self], proj);
map<B>(proj: (x: A) => B): Apply<Error, State, B> {
const self = this as any as Update<Error, State, A>;
return new Apply<Error, State, B>([self], proj);
}
mapTo<B>(value: B): Apply<State, B> {
const self = this as any as Update<State, A>;
return new Apply<State, B>([self], () => value);
mapTo<B>(value: B): Apply<Error, State, B> {
const self = this as any as Update<Error, State, A>;
return new Apply<Error, State, B>([self], () => value);
}
chain<B,State2>(andThen: (x: A) => Update<State2, B>): Chain<State&State2, B>;
chain<B>(andThen: (x: A) => Update<State, B>): Chain<State, B>;
chain<B>(andThen: (x: A) => Update<State, B>): Chain<State, B> {
const self = this as any as Update<State, A>;
return new Chain<State, B>(self, andThen);
chain<B,State2>(andThen: (x: A) => Update<Error, State2, B>): Chain<Error, State&State2, B>;
chain<B>(andThen: (x: A) => Update<Error, State, B>): Chain<Error, State, B>;
chain<B>(andThen: (x: A) => Update<Error, State, B>): Chain<Error, State, B> {
const self = this as any as Update<Error, State, A>;
return new Chain<Error, State, B>(self, andThen);
}
chainTo<B,State2>(value: Update<State2, B>): Chain<State&State2, B>;
chainTo<B>(value: Update<State, B>): Chain<State, B>;
chainTo<B>(value: Update<State, B>): Chain<State, B> {
const self = this as any as Update<State, A>;
return new Chain<State, B>(self, () => value);
chainTo<B,State2>(value: Update<Error, State2, B>): Chain<Error, State&State2, B>;
chainTo<B>(value: Update<Error, State, B>): Chain<Error, State, B>;
chainTo<B>(value: Update<Error, State, B>): Chain<Error, State, B> {
const self = this as any as Update<Error, State, A>;
return new Chain<Error, State, B>(self, () => value);
}
run(component: React.Component<any, State>, onNext?: (x: A) => void, onComplete?: () => void): () => void {
const self = this as any as Update<State, A>;
return go(self, component, onNext || noopFunc, onComplete || noopFunc);
run(component: React.Component<any, State>, onNext?: (x: A) => void, onError?: (x: Error) => void, onComplete?: () => void): Canceller {
const self = this as any as Update<Error, State, A>;
const getState = () => component.state;
const setState: SetState<State> = (projOrPatch, cb?) => component['updater'].enqueueSetState(component, projOrPatch, cb);
return go(self, getState, setState, onNext || noopFunc, onError || noopFunc, onComplete || noopFunc);
}
pending(): Update<State & { pending: boolean }, A> {
pending(): Update<Error, State & { pending: boolean }, A> {
// @ts-ignore
return patch({ pending: true }).chainTo(this.chain(value => patch({ pending: false }).mapTo(value)));
}
}
export function ap<State,A,B>(a: Update<State,A>, f: (a: A) => B): Update<State,B>;
export function ap<State,A,B,C>(a: Update<State,A>, b: Update<State,B>, f: (a: A, b: B) => C): Update<State,C>;
export function ap<State,A,B,C,D>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, f: (a: A, b: B, c: C) => D): Update<State,D>;
export function ap<State,A,B,C,D,E>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, f: (a: A, b: B, c: C, d: D) => E): Update<State,E>;
export function ap<State,A,B,C,D,E,F>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, e: Update<State,E>, f: (a: A, b: B, c: C, d: D, e: E) => F): Update<State,F>;
export function ap<State,A,B,C,D,E,F,G>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, e: Update<State,E>, f_: Update<State,F>, f: (a: A, b: B, c: C, d: D, e: E, f: F) => G): Update<State,G>;
export function ap(): Update<unknown, unknown> {
// SetState
export type SetState<State> = {
(patch: Partial<State>, cb?: () => void): void;
(proj: (x: State) => State, cb?: () => void): void;
};
export function ap<Error, State,A,B>(a: Update<Error, State,A>, f: (a: A) => B): Update<Error, State,B>;
export function ap<Error, State,A,B,C>(a: Update<Error, State,A>, b: Update<Error, State,B>, f: (a: A, b: B) => C): Update<Error, State,C>;
export function ap<Error, State,A,B,C,D>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, f: (a: A, b: B, c: C) => D): Update<Error, State,D>;
export function ap<Error, State,A,B,C,D,E>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, f: (a: A, b: B, c: C, d: D) => E): Update<Error, State,E>;
export function ap<Error, State,A,B,C,D,E,F>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, e: Update<Error, State,E>, f: (a: A, b: B, c: C, d: D, e: E) => F): Update<Error, State,F>;
export function ap<Error, State,A,B,C,D,E,F,G>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, e: Update<Error, State,E>, f_: Update<Error, State,F>, f: (a: A, b: B, c: C, d: D, e: E, f: F) => G): Update<Error, State,G>;
export function ap(): Update<unknown, unknown, unknown> {
const args = Array.prototype.slice.call(arguments, 0, arguments.length - 1);
const proj = arguments[arguments.length - 1];
return new Apply(args, proj);
}
export function of<A>(value: A): Pure<never, A> {
export function of<A>(value: A): Pure<never, {}, A> {
return new Pure(value);
}
export function patch<State>(patch: Partial<State>, callback?: () => void): Patch<State, void> {
export function patch<State>(patch: Partial<State>, callback?: () => void): Patch<never, State, void> {
return new Patch(patch, callback);
}
export function modify<State>(proj: (x: State) => State, callback?: () => void): Modify<State, void> {
export function modify<State>(proj: (x: State) => State, callback?: () => void): Modify<never, State, void> {
return new Modify(proj, callback);
}
export function effect<Error, Success>(eff: Eff<Error, Success>): Effect<{}, Either<Error, Success>> {
export function effect<Error, Success>(eff: Eff<Error, Success>): Effect<Error, {}, Success> {
return new Effect(eff as any);
}
export function batch<State>(...steps: Update<State, any>[]): Batch<State, void>;
export function batch<State>(steps: Update<State, any>[]): Batch<State, void>;
export function batch(): Batch<unknown, void> {
export function batch<Error, State>(...steps: Update<Error, State, any>[]): Batch<Error, State, void>;
export function batch<Error, State>(steps: Update<Error, State, any>[]): Batch<Error, State, void>;
export function batch(): Batch<never, unknown, void> {
const steps = Array.isArray(arguments[0]) ? arguments[0] : arguments;
return new Batch(steps);
}
export function concat<State>(...steps: Update<State, any>[]): Concat<State, void>;
export function concat<State>(steps: Update<State, any>[]): Concat<State, void>;
export function concat(): Concat<unknown, void> {
export function concat<Error, State>(...steps: Update<Error, State, any>[]): Concat<Error, State, void>;
export function concat<Error, State>(steps: Update<Error, State, any>[]): Concat<Error, State, void>;
export function concat(): Concat<never, unknown, void> {
const steps = Array.isArray(arguments[0]) ? arguments[0] : arguments;
return new Concat(steps);
}
export function difference<Error, State, A>(update: Update<Error, State, A>, diff: (prev: State, next: State) => State|Update<Error, State, void>): Difference<Error, State, A> {
return new Difference(update, diff);
}
class Pure<State, A> extends UpdateBase<State, A> {
class Pure<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly value: A,
) { super(); }
}
class Modify<State, A> extends UpdateBase<State, A> {
class Modify<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly proj: (x: State) => State,
readonly callback?: () => void,
) { super(); }
}
class Patch<State, A> extends UpdateBase<State, A> {
class Patch<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly patch: Partial<State>,
readonly callback?: () => void,
) { super(); }
}
class Get<State, A> extends UpdateBase<State, A> {
class Get<Error, State, A> extends UpdateBase<Error, State, A> {
}
class Children<State, A> extends UpdateBase<State, A> {
class Children<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly component: React.Component<any, any>,
readonly update: Update<any, A>
readonly update: Update<never, {}, A>
) { super(); }
}
class Effect<State, A> extends UpdateBase<State, A> {
class Effect<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly effect: Eff<never, A>,
readonly effect: Eff<Error, A>,
) { super(); }
}
class FromPromise<State, A> extends UpdateBase<State, A> {
class FromPromise<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly promise: () => Promise<A>,
) { super(); }
}
class Chain<State, A> extends UpdateBase<State, A> {
class Chain<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly update: Update<State, any>,
readonly andThen: (x: any) => Update<State, A>,
readonly update: Update<Error, State, any>,
readonly andThen: (x: any) => Update<Error, State, A>,
) { super(); }
}
class Apply<State, A> extends UpdateBase<State, A> {
class Apply<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly args: Update<State, any>[],
readonly args: Update<Error, State, any>[],
readonly proj: (...args) => A,
) { super(); }
}
class Batch<State, A> extends UpdateBase<State, A> {
class Batch<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly steps: Update<State, any>[],
readonly steps: Update<Error, State, any>[],
) { super(); }
}
class Concat<State, A> extends UpdateBase<State, A> {
class Concat<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly steps: Update<State, any>[],
readonly steps: Update<Error, State, any>[],
) { super(); }
}
class Difference<Error, State, A> extends UpdateBase<Error, State, A> {
constructor(
readonly update: Update<Error, State, A>,
readonly difference: (prev: State, next: State) => State|Update<Error, State, void>,
) { super(); }
}
// Functional helpers
const noopFunc = () => {};
export type Canceller = () => void;
export const get = new Get();
export const get = new Get<never, {}, void>();
export class Component<Props, State> extends React.Component<Props, State> {
export class Component<Error, Props, State> extends React.Component<Props, State> {
constructor(props, context) {
super(props, context);
// @ts-ignore
......@@ -321,24 +391,29 @@ export class Component<Props, State> extends React.Component<Props, State> {
state: ((prevState: Readonly<State>, props: Readonly<Props>) => (Pick<State, K> | State | null)) | (Pick<State, K> | State | null),
callback?: () => void
): void;
setState(update: Update<State, void>, callback?: () => void): void;
setState(update: Update<Error, State, void>, callback?: () => void): void;
setState(updateOrProjOrPatch, callback?) {
if (updateOrProjOrPatch instanceof UpdateBase) {
const onUpdate = this.props['onUpdate'] as unknown;
const update = this.prepareUpdate(updateOrProjOrPatch);
if (typeof(onUpdate) === 'function') {
onUpdate(updateOrProjOrPatch);
onUpdate(update);
} else {
updateOrProjOrPatch.run(this, callback);
update.run(this, callback);
}
return;
}
if (typeof(updateOrProjOrPatch) === 'function') {
return this.setState(new Modify<State, void>(updateOrProjOrPatch, callback))
return this.setState(new Modify<Error, State, void>(updateOrProjOrPatch, callback))
} else {
return this.setState(new Patch<State, void>(updateOrProjOrPatch, callback))
return this.setState(new Patch<Error, State, void>(updateOrProjOrPatch, callback))
}
}
prepareUpdate<A>(update: Update<Error, State, A>): Update<Error, State, A> {
return update;
}
}
export interface UpdateStatic {
......@@ -352,6 +427,7 @@ export interface UpdateStatic {
concat: typeof concat,
Component: typeof Component,
bind: typeof bind,
difference: typeof difference,
};
......@@ -366,28 +442,33 @@ export const Update = {
concat,
Component,
bind,
difference,
} as UpdateStatic;
export interface BoundStatics<State> {
ap<A,B,C>(a: Update<State,A>, b: Update<State,B>, f: (a: A, b: B) => C): Apply<State,C>;
ap<A,B,C,D>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, f: (a: A, b: B, c: C) => D): Apply<State,D>;
ap<A,B,C,D,E>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, f: (a: A, b: B, c: C, d: D) => E): Apply<State,E>;
ap<A,B,C,D,E,F>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, e: Update<State,E>, f: (a: A, b: B, c: C, d: D, e: E) => F): Apply<State,F>;
ap<A,B,C,D,E,F,G>(a: Update<State,A>, b: Update<State,B>, c: Update<State,C>, d: Update<State,D>, e: Update<State,E>, f_: Update<State,F>, f: (a: A, b: B, c: C, d: D, e: E, f: F) => G): Apply<State,G>;
batch(...steps: Update<State, any>[]): Batch<State, void>;
batch(steps: Update<State, any>[]): Batch<State, void>;
concat(...steps: Update<State, any>[]): Concat<State, void>;
concat(steps: Update<State, any>[]): Concat<State, void>;
of<A>(value: A): Pure<State,A>;
patch(patch: Partial<State>, callback?: () => void): Patch<State, void>;
modify(proj: (x: State) => State, callback?: () => void): Modify<State, void>;
effect<Error, Success>(eff: Eff<Error, Success>): Effect<State, Either<Error, Success>>;
get: Get<State, State>;
export interface BoundStatics<Error, State> {
ap<A,B,C>(a: Update<Error, State,A>, b: Update<Error, State,B>, f: (a: A, b: B) => C): Apply<Error, State,C>;
ap<A,B,C,D>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, f: (a: A, b: B, c: C) => D): Apply<Error, State,D>;
ap<A,B,C,D,E>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, f: (a: A, b: B, c: C, d: D) => E): Apply<Error, State,E>;
ap<A,B,C,D,E,F>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, e: Update<Error, State,E>, f: (a: A, b: B, c: C, d: D, e: E) => F): Apply<Error, State,F>;
ap<A,B,C,D,E,F,G>(a: Update<Error, State,A>, b: Update<Error, State,B>, c: Update<Error, State,C>, d: Update<Error, State,D>, e: Update<Error, State,E>, f_: Update<Error, State,F>, f: (a: A, b: B, c: C, d: D, e: E, f: F) => G): Apply<Error, State,G>;
batch(...steps: Update<Error, State, any>[]): Batch<Error, State, void>;
batch(steps: Update<Error, State, any>[]): Batch<Error, State, void>;
concat(...steps: Update<Error, State, any>[]): Concat<Error, State, void>;
concat(steps: Update<Error, State, any>[]): Concat<Error, State, void>;
of<A>(value: A): Pure<Error, State, A>;
patch(patch: Partial<State>, callback?: () => void): Patch<Error, State, void>;
modify(proj: (x: State) => State, callback?: () => void): Modify<Error, State, void>;
effect<Error, Success>(eff: Eff<Error, Success>): Effect<Error, State, Success>;
get: Get<Error, State, State>;
difference<A>(update: Update<Error, State, A>, diff: (prev: State, next: State) => State|Update<Error, State, void>): Difference<Error, State, A>;
Component: {
new <Props>(): Component<Error, Props, State>;
};
}
export function bind<State>() {
return Update as any as BoundStatics<State>;
export function bind<Error, State>() {
return Update as any as BoundStatics<Error, State>;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment