-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
low: the stream of conciseness 0.7+ roadmap #88
Comments
Working started: |
Backlog from api-0.6 implementation [] trigger should be touchable
[] value.touchable(initial) <- The ".from" construction not available for values with "initial" dependency requireds
[] signal.touchable(initial) [] v.as.readonly()
[] flow.resolve
[] flow as root level exportable factory function
[] .chan
[] value.trigger.from
[] value.trigger.flag.from
[] .map <- sysnonym for .view (on thinking)
[] x.group -- x.op -- x.block
x.block((ctx) => ({ // if returns non undefined
a: ctx.a,
b: ctx.a.select()
})).b.val
[] v.as.signal(), v.as.trigger.flag() |
Backlog: [] signal.trigger.resolved [] test case "should work signal.trigger with configured .pre" [] signal.trigger.from [] Add Loader example to documentation // Second example
const Loader = () => {
const count = value(0);
return {
start: count.updater(v => v + 1),
stop: count.updater(v => v - 1),
pending: count.select(v => v > 0)
}
} |
Join brainstorm: [] Add track|untrack for join with callback
Summary: [] No have similar for signal in 0.6 versions. value.from(() => ({ value(0).combine - have a problem with track|untrack semantic not for 0.6 version. I should make only value.combine. Aren't It? |
Backlog |
Backlog const v = value(0).filter().default(0) |
Backlog: const r = signal();
const u = signal<number>();
const v = value(0);
v.reset.by(r);
v.update.by(u, (state, up_value, up_value_prev) => {
return state + (up_value - (up_value_prev || 0));
});
u(10);
r(); And I think to very interesting idea is the convert "reset" function method to signal const a = value(0);
a.reset.to(() => console.log("reset")); |
proposal: localcosnt v = value(0);
v.flow((value) => {
const ticket = local(() => wait(k));
if (!ticket.val) return flow.stop();
return value;
});
v.flow.filter(() => {
return local(() => wait(k)).val
});
local(expr) // <- bind only first time not necessary bind it every time
// if you need bind every time you should use plain function not local expression.
// synonim of local(expr, []);
local(expr, [<real value dep>, ...])
local(expr, <refresh symbol or expression>);
// or all combination of that
local(expr, <refresh symbol or expression>, [<real value dep>, ...]);
local(expr, [<real value dep>, ...], <refresh symbol or expression>); |
proposal: waitconst v = value(0);
const s = signal(0);
// wait.all([v, s]) - it is a ready (trigger)
await wait.all([v, s]).promise
await wait.once([v, s]).promise
wait(v) // similar to signal.trigger.from(v) or v.to.signal.trigger() const k = signal(0);
const m = value(0);
v.flow.wait(m)
.flow.wait.all(k, m) // or synonim to .flow.wait.all([k, m]) -- think about
.flow.wait.race(k, m)
.to((v) => console.log(v)); const k = signal(0);
const m = value(0);
v.flow.wait(m, (v_val, m_val) => return v_val + m_val)
.flow.wait.all([k, m], (v_val, [k_val, m_val]) => return v_val + k_val + m_val) // or stop of course (maybe I can get resolve???)
.flow.wait.race([k, m], (v_val, [k_val, m_val], resolve, v_val_prev) => {});
.to((v) => console.log(v)); |
up: replace useMemo by useRef
|
proposal: chainThink about "chain" abstraction const a = value(0);
const b = chain(() => a.val);
assert(b.val === undefined);
a(5);
assert(b.val === undefined);
b.input.to(b.output); // "to" better than "watch"
a(6);
assert(b.val === 6); |
proposal: pool.flatprivate finish_purchase = pool(async () => {
if (this.finish_purchase.count > 1) return; // not count.val here
}); const a = pool(async () => {
if (a.count.val > 1) return a.threads[0].promise; // Return promise from first thread
}
assert(a.flat.pending === false);
assert(chan(async () => {}).flat.pending === false); |
proposal: signal.unionCollect signals from several values and signals. const a = value(0);
const b = signal(0);
const u = signal.union(a, b);
u.val === 0;
a(5);
u.val === 5;
b(10);
u.val === 10; |
proposal: chanChan - is therm for define async flow. const a = chan(async (p, k, m) => {}, empty_val?);
a(1,2,3);
const b = a.chan(async (a_val, prev_a_val) => {
});
chan(async () => {})
.flow.filter()
.flow.filter.untrack(() => this.loaded)
.watch.once(() => {});
chan.untrack(async () => {})
.join(b)
.select(([a,b]) => a + b)
.flow.untrack(() => {}, empty_val?)
.watch(() => {})
chan(async () => {}).wrap.filter.not()
// ...
chan.untrack(async () => {}, empty_val)
value.from(() => {})
.chan(async () => {}, empty_val)
.flow.untrack(() => {}, empty_val?)
.flow((a) => a + b.val)
.watch(() => {}); Chan is not a "pool" because "chan" provides only one value, and flows around that value. Overwise "pool" provides a group of parallel or series async execution processes. But both these meanings can be used with similar async operators. const t = value(0)
.view(() => {})
.wrap.throttle(300)
.chan.debounce(300);
// .flow.debounce(300); ??
// const t = pool(async () => {}) // "pool" untracked by default for safety reason
// .wrap.throttle // hmmm multiples of parameters and throttle can be strange combination.
// Necessary to think about "throttle" and "debounce" for "pool". Possible typecast syntax chan(async () => {}).select.untrack(() => {});
signal(0).to.value() // vs "signal(0).to.value"
value(0).to.signal()
signal(0).to.ready()
signal(0).promise
value(0).promise
value(0).to.ready().promise // promise recreate on demand // vs value(0).to.ready.promise
value.flag()
value.flag.not()
value.ready()
signal.flag() // equivalent to "value.flag"
signal.ready()
// Think about rename "value.ready" to "value.once" or "value.trigger" ...
signal(0).to.trigger() // The primary candidate for changing to
signal(0).to.once() // hmm..
|
low: proposal pool single and pool static chaining factorypool.single(async () => {
})
// or
pool(async () => {
}).single()
// Will be same result pool.debounce(300).single().flow(async () => {});
// ok |
low: proposal flow async and error with unhandled flow unsubconst a = value(0);
const b = value(0);
const c = value(0);
// For a first error on next cases
a.flow.async(async (a) => {
const data = b.flow(() => {}).val; // Error here, necessary for using isolate here
}};
// Flow async proposal
const s = a.flow.async(async (a) => {
if (!a) return stoppable.stop();
return await load_subscriptions();
});
s.val // subscriptions or undefined
s.initialized.val
s.error.val
s.pending()
a.flow.async.debounce(300)
.pipe(
customDebounce,
async (a) => {
if (!a) return stoppable.stop();
const m = c.val; // Subscrible to m or not??
return await load_subscriptions(m);
}
); // readonly async flow
const all_ok = value.combine(a, s.initialized).select(([a, s]) => a && s) // or value.combine([a, s.initialized])
// Rename pool to asyncs)))
// Constructors
value.async(async (v) => {});
signal.async(async (v) => {});
a.flow.async(async (a) => {});
const m = a.flow.async(async (a) => {}).single();
const h = a.sub.async(reactionable, async (state, val) => {}); // h.pending.val
const h_2 = pool(async (a,b,c,d,e) => {
stoppable.stop();
});
// Syntax possibilities
a.async
.debounce(300)
.pipe(
customDebounce,
async (a) => {
if (!a) return stoppable.stop();
const m = c.val; // Subscrible to m or not?? (untrack inside pipe section... hmmmm)
return await load_subscriptions(m);
}
)
.flow(async (a) => {
return await load(a, b.val, c.val); // Depend on change any of that values
})
; // readonly async flow
// Pipe <> Flow same words for not obvious differences... hmm
const s = flow.async(async () => {
return await load(a.val, b.val, c.val);
}); // Syntax possibilities
const t = a.async
.debounce(300)
.flow.untrack(
customDebounce,
async (a) => {
if (!a) return stoppable.stop();
const m = c.val; // not subscribe to c
return await load_subscriptions(m);
}
)
.flow(async (a) => {
return await load(a, b.val, c.val); // subscribe to b and c
})
; // readonly async flow
t.val
t.ready.val
t.error.val
t.pending.val
// flow factory (only track, untrack unsupported)
const s = flow.async(async () => {
return await load(a.val, b.val, c.val);
});
const f_sync = flow(() => {
return sync_load(a.val, b.val, c.val);
});
// And pool
const p = pool(async (a,b,c,d,e) => {
stoppable.stop();
});
p.pending.val |
low: proposal pool syntaxconst a = pool(async () => {});
a.single()
a.debounce(300)
a.throttle()
// or
a.pipe.single()
a.pipe.debounce(300)
a.pipe( ... ) // what is It? and for values and signals const a = value(0);
a.pool.debounce(100) // what is it?
|
low: on((stop) => {}, () => {}) implement |
low: try deprecate stopppableselector(init_value?, (stop) => {});
selector((stop?) => {});
cycle((stop) => {});
on((stop) => {}, () => {});
// But I think pool no need to use "(stop) =>" syntax, maybe stoppable() is better for pool?
// pool.stop
pool(async () => {
const stop = pool.stop;
pool.stop();
pool.stop.throw();
});
pool.stoppable((stop) => {
return async () => {
}
});
|
low: signal.trigger.from methodShould be added: |
(Declined) proposal: signal.flag and value.flag const flag = signal(false).pre((v) => !!v); |
ver very low: proposal proxy propertyconst v = value({});
v.proxy.a = 10;
v.update.proxy(p => { // one transaction
p.b = (p.a += 15);
}); |
proposal: name for production ready version "Universal data-flow" |
low: think about "in one transaction" featureconst s = signal();
const v = value();
on(s, (s_v) => v.val += s_v);
on(() => [s,v], console.log);
on.transaction(s, (s_v) => v.val += s_v); // used one transaction with changed s
// Think about |
proposal: queue implementationconst q = queue<Type?>(init?: Type[]);
// q(10); // Think about queue - is a reactive value with set of elements or is the signal for add next one... Hmm.
// q.queue // []
q.front // top
q.back // last element
// q.all // array of elements in queue
q.size
q.clear()
//
q.val
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.clear()
// Support queue for q.wrap and q.view // think about api
// val: <array_of_elements> readonly
// q(<new_element>)
Or q() - no possible, but
q.add(value: Type)
q.val: Type
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.clear()
// How I can make queue with limited size same as in my swipe impl?
q = queue();
q(values: Type[]);
q.set(values: Type[]);
q.update(...);
q.val: Type;
q.add(value: Type);
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.empty: Value
q.clear()
//
const t = q.first
t.val: Type
t.started: Value
t.start();
t.release(); // remove item from queue (or dequeue)
// or
t.val: Type
t.start: ready(false).to(true)
t.remove(); // remove item from queue // or ready(false).to(true) too Decorating add signal const q = queue<Type>([], (queue, value: Type) => {
const excluded = queue.slice(3);
// const data = queue..
// How to convert value to queue element?
});
const q = queue<Type>();
q.before (Signal)
q.before((value) => {
stoppable.stop();
transaction
check q
remove unnecessary q elems
add new
finish transaction
});
|
proposal: interceptor methodconst a = value(5);
const b = value(1);
const unsub = a.interceptor((new_value, current_value, stop?) => {
if (new_value == 1) {
b.set(10);
stop();
return;
}
return current_value + new_value;
});
// stoppable supported
a(1); // b.val === 10, a.val === 5
a(2); // a.val === 7 Think about syntax for const v = value(0);
const s = signal(0);
v.sub.intercept(s, (v_value, new_signal_value, stop ) => {
///
});
v.intercept.sub // alias
// or
v.sub.intercept(s, (v_value, new_signal_value, prev_signal_value ) => {
stoppable.stop();
});
v.intercept.sub // alias
|
proposal: pool async oparatorspool.single(async ({ cancelled }) => {
const zip = await search();
if (cancelled()) return;
return await unpack(zip);
});
// If exists active promise each query return It
// pool.throttle(150, async ({ cancelled, abortController?.., cancel }) => {
pool.throttle(150, async ({ stopped, abortController?.., stop }) => {
const zip = await search();
if (zip === 0) stop();
if (stopped()) return;
return await unpack(zip);
});
pool.debounce(150, async function * ({ cancel }) {
const zip = yield search();
if (zip === 0) cancel();
return yield unpack(zip);
}); It is offtopic, but I want to think about throttle and debounce for signals: const start = signal();
const delayedStart = signal.throttle(150, start);
const a = value();
const delayedA = value.throttle(150, a);
const p = pool(async () => await search());
const delayedP = pool.throttle(150, p); Maybe only one |
low: external package realar-form |
very low: add jsx methodconst a = value(0);
return (
<p>{a.jsx}</p>
)
return (
<p>{a.jsx(v => v.map((i, k) => <i key={k}>{i}</i>))}</p>
) 😉
For making super performant interfaces 👍 |
very low: state machine implThis abstraction is necessary for the implementation of serial operations. Reactions on each section of the serial operations. const m = machine()
.step(a, () => {})
.step(b, () => 0)
.step(machine.oneOf()
.select(a, () => {})
.select(b)
.select(c), () => {})
.loop(); But I think the yield generator function is an interesting way of decision. loop(function *() {
yield a;
yield b;
yield loop.oneOf([a, b, c]) // or loop.oneOf().select(a, () => {}).select(b, () => {})... chain function
yield loop.race([a, b, c])
yield loop.all([a, b, c])
// Promise namespace operations for suggest
}); |
low: readonly methodconst v = value(0);
const readonly_v = v.readonly(); |
Should research about argument passing to "prop" decorator, for the easy binding reactive container to class property: class A {
@prop(this.store) flat_store
} |
proposal: value.extractIt should be extract object or array of reactive containers to object or array of usual javascript values. const email = value('a@x');
const values = value.extract({ email });
console.log(values.email) // [email protected] Possible kinds of name: "values" consistent with |
proposal: input as exportable api functioninput.handler <- (ev) => ev.target.value
// proposal for another names.
// input.delegate
// input.change
// input.event
input.valid
input.valid.pending
input.validator(
signal<string>().map((state) => /a/.test(state))
)
// input.validator.async() // or
// Interested but no consistency to other code base.
// input.validator((signal) => {
// return signal.as.chan().map(async () => {})
// });
// Final perfect version
input.validator([
signal<string>().filter() <-- what will happens if validator will be stopped
chan<string>().map(async (state) => await fetch_is_valid(state)),
(state) => /aa/.test(state),
async (state) => await ext_test(state)
]); proposal: remove select.multipleImprove "select" to "select.multiple" signature const s = select({ a: (state) => state.a }); The argument can be function, reactive container, object map, or array tuple. proposal: reactive container as argument of "map" (and array of)const pipe_double = signal<number>().map(state => state * 2);
value(1).map(pipe_double).val // 2
value(1).map([pipe_double, pipe_double]).val // 4
// Use signature as an array or as reactive container, or usual function signature. |
low: test ts typingshttps://github.com/reduxjs/reselect/blob/master/typescript_test/test.ts#L479 // typings:expect-error
const baz2: string = baz; |
fix: update component warn from modification in the render functionhttps://codesandbox.io/s/realar-api-unsubscribe-scopes-control-0sziu?file=/src/App.tsx
|
brainstorm: signals two directional synchronizationconst a = signal<number>();
const b = signal<number>();
sync(a, b);
sync(b, a); Signals connection without the infinity loop error I should realize a way to the bi-directional connection between signals. const a = signal(0);
const b = signal(0);
sync.bi(a, b);
a.val++
b.val++
assert(a.val === 2) |
signal.from.select broken ts types with tuplessignal
.from(() => {
return [
buyAnnualLocale.val,
sharedPurchases().annual_subscription.val
]
})
.select(([locale, sub]) => { // both variables have one type "string | Subscription | undefined"
if (!sub) return void 0;
const localized_price = (sub as any)?.localizedPrice;
if (!localized_price) return void 0;
return locale.text
}) |
value(false) broken types for ts <4const y: Value<boolean> = value(false);
/*
Type 'Value<false, false>' is not assignable to type 'Value<boolean, boolean>'.
Type 'Value<false, false>' is not assignable to type '{ (value: boolean): void; set(value: boolean): void; }'.
Types of parameters 'value' and 'value' are incompatible.
Type 'boolean' is not assignable to type 'false'.ts(2322)
*/ |
proposal: manual rerun cycle outsideconst h = cycle(() => {});
h.stop();
h.run(); |
proposal: shortcut notsharedEye().enabled
.as.signal()
.filter()
.filter(isNext.map(flag => !flag)) // TODO: shortcut not isNext.not() |
proposal: value with no initial parameterexport const swipeAnimDirection = value<undefined | 'up' | 'down'>(); |
proposal: typings improvement for filter value<void | number>()
.as.signal()
.filter() // Should remove undefined | null | void from Will signal type
.to(onlyNumberSignal); |
proposal: new Scope apiconst MyLogic = (initial: string) => {}
const MyScope = scope(MyLogic)
const A = () => {
const B = useJsx(() => {
const logic = useScoped(MyLogic)
/* proposal: use scope logic from local section
useLocal(() => {
const logic = scoped(MyLogic) // Warning scope recreation
const logic = scoped.current(MuLogic) // Think about
});
*/
return
});
return <MyScope initial={'hello'}><B>{children}</B></MyScope>
}
|
fix: callback of sync or to methods should be untracked ... hmm.. check itloader_queue.sync(async queue => {
// should be untracked here
}) |
fix: typings for select without parameter const toggle = value(false).pre((_, state) => !state);
const enabled = toggle.select(); // Incorrect: ValueReadonly<unknown> |
proposel: value.willconst t = value.will<number>();
expect(t.val).toBeUndefined();
t.map(n: number => n + 1); |
proposalconst takeUser = value();
const takeAuth = value();
takeAuth.onChange(takeUser.andUpdate)
takeUser.andSet()
takeUser() - alias for takeUser.andGet
const start = signal();
start.get - doesn't allowed
start.fire(new_value)
takeUser.onChange(start.fire)
start.getValue() - returns value instance
isDirty, isUndefined, isTouched
All methods created only by demand.
start.flow
start.pre
takeUser.compose |
"ready" -> "trigger"
"sub" -> "update.by"
The text was updated successfully, but these errors were encountered: