Commit 469d3ed0 by Vladislav Lagunoff

Добавлен ~/update

parent 11e62be8
tsconfig.json
/**/node_modules
/**/yarn-error.log
\ No newline at end of file
import { Eff } from './';
import Monad from 'burrido';
// Do notation
export const { Do } = Monad({
pure: Eff.of,
bind: (m, proj) => m.chain(proj),
});
Eff.Do = Do;
declare module "./index" {
interface EffStatics {
Do<Error, Success>(iter: () => IterableIterator<Eff<Error, Success>>): Eff<Error, Success>;
}
}
......@@ -45,12 +45,11 @@ export class EffBase<Error, Success> {
return new Apply([this], proj);
}
chain<Success2>(andThen: (x: Success) => Eff<Error, Success2>): Eff<Error, Success2>;
chain<Error2, Success2>(andThen: (x: Success) => Eff<Error2, Success2>): Eff<Error|Error2, Success2>;
chain<Error2, Success2>(andThen: (x: Success) => Eff<Error2, Success2>): Eff<Error|Error2, Success2> {
return new Chain(this.toEff(), (ethr: Either<Error, Success>) => {
return ethr.fold<Eff<Error|Error2, Success2>>(failure, andThen);
});
chain<Success2>(andThen: (x: Success) => Eff<Error, Success2>): Chain<Error, Success2>;
chain<Error2, Success2>(andThen: (x: Success) => Eff<Error2, Success2>): Chain<Error|Error2, Success2>;
chain<Error2, Success2>(andThen: (x: Success) => Eff<Error2, Success2>): Chain<Error|Error2, Success2> {
// @ts-ignore
return new Chain(this.toEff(), ethr => ethr.fold(failure, andThen));
}
chainTo<Success2>(value: Eff<Error, Success2>): Eff<Error, Success2>;
......@@ -80,17 +79,17 @@ export class EffBase<Error, Success> {
}
export function of<A>(value: A): Eff<never, A> {
export function of<A>(value: A): Pure<never, A> {
return new Pure(either.success(value));
}
export function success<A>(value: A): Eff<never, A> {
export function success<A>(value: A): Pure<never, A> {
return new Pure(either.success(value));
}
export function failure<A>(value: A): Eff<A, never> {
export function failure<A>(value: A): Pure<A, never> {
return new Pure(either.failure(value));
}
......@@ -148,8 +147,8 @@ export function record<R extends Record<string, Eff<any, any>>>(rec: R): Eff<{ [
/** Traverse an array */
export function traverse<ERR, A, B>(array: A[], f: (a: A, idx: number) => Eff<ERR, B>): Eff<ERR, B[]> {
if (array.length === 0) return success([]);
export function traverse<Error, A, B>(array: A[], f: (a: A, idx: number) => Eff<Error, B>): Eff<Error, B[]> {
if (array.length === 0) return success<B[]>([]);
return ap.apply(undefined, [...array.map(f), (...args) => args]);
}
......@@ -262,7 +261,7 @@ export function go<Error, Success>(effect: Eff<Error, Success>, onNext: (x: Eith
if (effect instanceof Apply) {
let allInitialized = false;
let subscriptions: Array<Function|null>;
let subscriptions: Array<Function|undefined|null> = new Array(effect.args.length);
const initializedFlags: Array<true|undefined> = new Array(effect.args.length);
const recentValues: unknown[] = new Array(effect.args.length);
const next = idx => result => {
......@@ -280,7 +279,11 @@ export function go<Error, Success>(effect: Eff<Error, Success>, onNext: (x: Eith
for (const unsub of subscriptions) if (unsub !== null) return;
onComplete();
};
subscriptions = effect.args.map((eff, idx) => go(eff, next(idx), complete(idx)));
effect.args.forEach((eff, idx) => {
const canceller = go(eff, next(idx), complete(idx));
if (subscriptions[idx] !== null) subscriptions[idx] = canceller;
});
return () => subscriptions.forEach(
funOrNull => funOrNull ? funOrNull() : void 0
......@@ -352,7 +355,26 @@ export abstract class HasEffect<Error, Success> extends EffBase<Error, Success>
export const noop: Cmd<never> = new Batch([]);
export default {
export interface EffStatics {
of: typeof of,
success: typeof success,
failure: typeof failure,
fromCallback: typeof fromCallback,
fromPromise: typeof fromPromise,
fromPromise_: typeof fromPromise_,
fromEither: typeof fromEither,
thunk: typeof thunk,
lazy: typeof thunk,
ap: typeof ap,
record: typeof record,
batch: typeof batch,
concat: typeof concat,
forget: typeof forget,
go: typeof go,
noop: typeof noop,
}
export const Eff = {
of,
success,
failure,
......@@ -369,8 +391,5 @@ export default {
forget,
go,
noop,
};
} as EffStatics;
/// monadic do-notaion https://github.com/pelotom/burrido
// import Monad from 'burrido'
// export const Do: <err,a>(iter: () => IterableIterator<Eff<err,a>>) => Eff<err,a> = Monad({ pure: of, bind: chain }).Do;
......@@ -67,7 +67,6 @@ export class HttpEffect<A> extends HasEffect<HttpError, A> {
/** send a request */
export function send(req: RequestProgress): HttpEffect<Either<Progress, Response>>;
export function send(req: Request): HttpEffect<Response>;
export function send(req: Request|RequestProgress): HttpEffect<unknown> {
return new HttpEffect(req);
......@@ -75,9 +74,9 @@ export function send(req: Request|RequestProgress): HttpEffect<unknown> {
/** shortcut for GET requests */
export function get(url: string, request?: Omit<RequestProgress, 'url'|'method'>): HttpEffect<Either<Progress, Response>>;
export function get(url: string, request?: Omit<Request, 'url'|'method'>): HttpEffect<Response>;
export function get(url: string, request?: Omit<Request|RequestProgress, 'url'|'method'>): HttpEffect<unknown> {
//export function get(url: string, request?: Omit<RequestProgress, 'url'|'method'>): HttpEffect<Either<Progress, Response>>;
export function get(url: string, request?: Omit<Request, 'url'|'method'>): Eff<HttpError, Response>;
export function get(url: string, request?: Omit<Request|RequestProgress, 'url'|'method'>) {
return send({ ...request, method: 'GET', url });
}
......
import { Update } from './';
import Monad from 'burrido';
// Do notation
export const { Do } = Monad({
pure: Update.of,
bind: (m, proj) => m.chain(proj),
});
Update.Do = Do;
declare module "./" {
interface UpdateStatic {
Do<State, Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>;
}
interface BoundStatics<State> {
Do<Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>;
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
</head>
<body>
<div id="app"/>
</body>
<script type="text/javascript" src="entry.bundle.js"></script>
</html>
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Update } from '../';
import '../burrido';
import * as http from '../../http';
import { InputHTMLAttributes } from 'react';
type Props = {
};
type State = {
pending: boolean;
search: string;
response?: http.Response;
};
const U = Update.bind<State>();
class Widget extends Update.Component<Props, State> {
state: State = { search: '', pending: false };
handleNameChange: InputHTMLAttributes<HTMLInputElement>['onChange'] = (e) => this.setState({ search: e.target.value });
handleSubmit = (e: React.FormEvent) => this.setState(U.Do<void>(function*() {
const { search: q }: State = yield U.get;
const response = yield U.effect(http.get(http.join('https://api.github.com/search/repositories', { q }))).pending();
yield U.patch({ response });
}));
render() {
const { search, response, pending } = this.state;
return <div>
<form onSubmit={e => (e.preventDefault(), !pending && this.handleSubmit(e))}>
<input value={search} onChange={this.handleNameChange} placeholder="Github search"/>
<button type="submit" disabled={pending}>{pending ? 'Please, wait…' : 'Go'}</button>
</form>
<textarea value={JSON.stringify(response)} readOnly/>
</div>;
}
}
ReactDOM.render(<Widget/>, document.getElementById('app'));
{
"name": "example",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"react": "^16.6.3",
"react-dom": "^16.6.3",
"ts-loader": "3",
"typescript": "^3.1.6",
"webpack": "3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "2"
}
}
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const tsConfig = require('./tsconfig.json');
module.exports = function(env={}) {
const entry = env.entry || './index.tsx';
const output = path.resolve(__dirname);
return {
context: path.resolve(__dirname),
entry: { entry: [entry], },
output: {
path: output,
filename: '[name].bundle.js',
},
module: {
loaders: [{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
compilerOptions: tsConfig.compilerOptions,
transpileOnly: env.hasOwnProperty('transpileOnly') ? env.transpileOnly : true,
},
}],
},
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
alias: {
react: path.resolve(__dirname, 'node_modules/react'),
},
},
};
};
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
{
"name": "update",
"version": "1.0.0",
"main": "index.ts",
"license": "MIT",
"peerDependencies": {
"react": "^16.6.3"
},
"dependencies": {
"@types/react": "^16.7.6"
},
"optionalDependencies": {
"burrido": "^1.0.8"
}
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/prop-types@*":
version "15.5.6"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c"
"@types/react@^16.7.6":
version "16.7.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.6.tgz#80e4bab0d0731ad3ae51f320c4b08bdca5f03040"
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
burrido@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/burrido/-/burrido-1.0.8.tgz#b29684a5486ab3301149147983c006933847f324"
dependencies:
immutagen "^1.0.0"
csstype@^2.2.0:
version "2.5.7"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.7.tgz#bf9235d5872141eccfb2d16d82993c6b149179ff"
immutagen@^1.0.0:
version "1.0.8"
resolved "https://registry.yarnpkg.com/immutagen/-/immutagen-1.0.8.tgz#efc32eccab30a833496de43a4ea7aa4353e1097a"
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