-
Notifications
You must be signed in to change notification settings - Fork 23
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
Make consistent API decision about named properties #193
Comments
I took all the current operators and looked at what they might look like if we preferred named params vs. positional ones. For most of our operators, either would be fine. For the mathematical ones ( I want to think about this some more/get input from other developers before making a decision here. For now, I'm going to err towards positional arguments and passing a dictionary as the final value when needed. Named params(names that don't aid understanding are commented out) stream
// .appendUnit({unit: 'px'})
.dedupe({areEqual: deepEqual})
// .delayBy({time: 100})
// .distanceFrom({origin: {x: 0, y: 0}})
// .ignoreUntil({expectedValue: 100})
.inverted()
// .isAnyOf({matches: [1, 2, 3]})
.log({label: 'current location', path: 'x'})
.clamp({lowerBound: 0, upperBound: 100})
// .merge({others: [a$, b$]})
// .dividedBy({denominator: 100})
// .addedTo({amount: {x: 0, y: 100}})
// .pluck({path: 'y'})
.rewrite({
map: {[NavState.CLOSED]: true, [NavState.OPEN]: false},
dispatchOnKeyChange: false
})
.rewriteRange({fromStart: 0, fromEnd: 1, toStart: 50, toEnd: 100})
// .rewriteTo({value: 100})
// .multipliedBy({coefficient: 20})
.slidingWindow({size: 5})
// .startWith({value: 1})
// .threshold({limit: 50})
.thresholdRange({start: 20, end: 30})
.timestamp()
.velocity({pulse: dragAtRest$})
._debounce({pulse: frame$})
// ._filter({predicate: isNaN})
._flattenIterables()
// ._map({transform: (value) => value ** 2})
._multicast()
// ._nextOperator({operation({value, nextChannel}) => nextChannel(value)})
// ._reactiveMap({transform: (a, b) => a * b, args: [a$, b$]})
._reactiveNextOperator(
{operation({value, nextChannel}) => nextChannel(value), args: [a$, b$], waitForAllValues: true})
._read()
._remember()
// ._tap({transform: (value) => sideEffect(value)}) Named params(Names that are make the API less usable are commented) stream
.appendUnit({unit: 'px'})
.dedupe({areEqual: deepEqual})
.delayBy({time: 100})
.distanceFrom({origin: {x: 0, y: 0}})
.ignoreUntil({expectedValue: 100})
.inverted()
// .isAnyOf({matches: [1, 2, 3]})
.log({label: 'current location', path: 'x'})
.clamp({lowerBound: 0, upperBound: 100})
// .merge({others: [a$, b$]})
// .dividedBy({denominator: 100})
// .addedTo({amount: {x: 0, y: 100}})
.pluck({path: 'y'})
.rewrite({
map: {[NavState.CLOSED]: true, [NavState.OPEN]: false},
dispatchOnKeyChange: false
})
.rewriteRange({fromStart: 0, fromEnd: 1, toStart: 50, toEnd: 100})
// .rewriteTo({value: 100})
.multipliedBy({coefficient: 20})
.slidingWindow({size: 5})
// .startWith({value: 1})
.threshold({limit: 50})
.thresholdRange({start: 20, end: 30})
.timestamp()
.velocity({pulse: dragAtRest$})
._debounce({pulse: frame$})
// ._filter({predicate: isNaN})
._flattenIterables()
// ._map({transform: (value) => value ** 2})
._multicast()
._nextOperator({operation({value, nextChannel}) => nextChannel(value)})
._reactiveMap({transform: (a, b) => a * b, args: [a$, b$]})
._reactiveNextOperator(
{operation({value, nextChannel}) => nextChannel(value), args: [a$, b$], waitForAllValues: true})
._read()
._remember()
._tap({transform: (value) => sideEffect(value)}) Positional args(arguments that would be easier to read with names are commented) stream
.appendUnit('px')
// .dedupe(deepEqual)
.delayBy(100)
.distanceFrom({x: 0, y: 0})
.ignoreUntil(100)
.inverted()
.isAnyOf([1, 2, 3])
.log('current location', 'x')
.lowerBound(0)
.upperBound(100)
.merge([a$, b$])
.dividedBy(100)
.addedTo({x: 0, y: 100})
.pluck('y')
// .rewrite(
{[NavState.CLOSED]: true, [NavState.OPEN]: false},
{dispatchOnKeyChange: false})
.rewriteRange({fromStart: 0, fromEnd: 1, toStart: 50, toEnd: 100})
.rewriteTo(100)
.multipliedBy(20)
// .slidingWindow(5)
.startWith(1)
.threshold(50)
.thresholdRange(20, 30)
.timestamp()
// .velocity(dragAtRest$)
// ._debounce(frame$)
._filter(isNaN)
._flattenIterables()
._map((value) => value ** 2)
._multicast()
._nextOperator((value, nextChannel) => nextChannel(value))
._reactiveMap((a, b) => a * b, [a$, b$])
// ._reactiveNextOperator((value, nextChannel) => nextChannel(value), [a$, b$], { waitForAllValues: true })
._read()
._remember()
._tap((value) => sideEffect(value)) Positional args(operators that would need a dictionary anyway are commented) stream
.appendUnit('px')
.dedupe(deepEqual)
.delayBy(100)
.distanceFrom({x: 0, y: 0})
.ignoreUntil(100)
.inverted()
.isAnyOf([1, 2, 3])
.log('current location', 'x')
.lowerBound(0)
.upperBound(100)
.merge([a$, b$])
.dividedBy(100)
.addedTo({x: 0, y: 100})
.pluck('y')
// .rewrite(
{[NavState.CLOSED]: true, [NavState.OPEN]: false},
{dispatchOnKeyChange: false})
// .rewriteRange({fromStart: 0, fromEnd: 1, toStart: 50, toEnd: 100})
.rewriteTo(100)
.multipliedBy(20)
.slidingWindow(5)
.startWith(1)
.threshold(50)
.thresholdRange(20, 30)
.timestamp()
.velocity(dragAtRest$)
._debounce(frame$)
._filter(isNaN)
._flattenIterables()
._map((value) => value ** 2)
._multicast()
._nextOperator((value, nextChannel) => nextChannel(value))
._reactiveMap((a, b) => a * b, [a$, b$])
// ._reactiveNextOperator((value, nextChannel) => nextChannel(value), [a$, b$], { waitForAllValues: true })
._read()
._remember()
._tap((value) => sideEffect(value)) |
If we go with arrays, we should be consistent there too (e.g. change the signature of |
Summary: Formerly, `switchMe$.rewrite({ a: a$, b: b$ })` wouldn't dispatch anything until both `a$` and `b$` had dispatched, event if `switchMe$` had only dispatched `a`. This resolves that issue. The solution was to add an option to `_reactiveNextOperator` and `combineLatest` to determine whether or not they should wait until all children have dispatched before themselves dispatching. In order to add an option to `_reactiveNextOperator`, its signature needed to change. Instead of accepting its arguments as positional arguments, it now accepts them in a single list. This is consistent with our other APIs like `isAnyOf`. To support strong typing, it still passes values into its `operation` positionally. `...` must come at the end of a function signature, but not a function call. Thus, this shouldn't cause any issues. Normally for a change of this magnitude, I'd write a codemod, but since I don't believe anyone but me has written code against these APIs, I've made the changes by hand in my own projects. There's a bigger API design question (should we use named args everywhere, or pass options dictionaries to APIs like `_reactiveNextOperator`?) captured in #193 (comment) Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, featherless Reviewed By: O2 Material Motion, #material_motion, featherless Subscribers: featherless Tags: #material_motion Differential Revision: http://codereview.cc/D3318
Glancing through these again, and seeing how many operators I've added ReactiveMappableOptions too, I'm tempted to go back to named args. Need to get the types right first, so I can make sure the named args implementation is as DRY as possible. |
This makes sense for operators and interactions. I'll hold off on changing |
Summary: This is the first step towards #193. A nice side effect of this change in conjunction with D3392 is that the types on `transform` don't have to be manually specified - TypeScript can correctly infer them. It also makes calls to `reactiveMap` more readable, because the second argument is no longer an unnamed dictionary. As part of this change, the signature of the operation in `_reactiveNextOperator` is now a higher-order function. The root cause of D3383 was that there was no closure for an individual observer - state was either ephemeral for an individual emission or shared across all observers. By passing `emit` and `values` separately, there is now a scope where an operator can store observer-level state. In my next pass at #193, I will update `_nextOperator` to use this same signature. In fact, I wonder if `_nextOperator` and `_reactiveNextOperator` can be merged entirely. As an additional part of #193, I'm changing the name of `dispatch` to `emit` across the codebase. Something that has been emitted can naturally be called an emission. There's no good noun to refer to something that has been dispatched. I expect having an easy way to refer to each item in a stream will make documentation more clear. I typically abide by the one-small-change-per-diff policy, but since changing to named args is such a massive refactoring, it's a good time to make these smaller changes too. It reduces the number of refactoring passes over the codebase. Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, featherless Reviewed By: O2 Material Motion, #material_motion, featherless Subscribers: featherless Tags: #material_motion Differential Revision: http://codereview.cc/D3395
There are some operators where named args make the API worse: pluck, isAnyOf, added/subtracted/multiplied/dividedBy, distanceFrom, etc. I wonder if I should add a positional signature for those too. Every operator would accept named args, but a few would also accept a positional alternative. |
🎊 My diff is ready for review at http://codereview.cc/D3419 |
Summary: A continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, featherless Reviewed By: O2 Material Motion, #material_motion, featherless Tags: #material_motion Differential Revision: http://codereview.cc/D3398
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, featherless Reviewed By: O2 Material Motion, #material_motion, featherless Tags: #material_motion Differential Revision: http://codereview.cc/D3399
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3400
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3401
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3402
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3403
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3404
Summary: Continuation of #193 Since we are using named args now, it doesn't make sense to have separate `upperBound` and `lowerBound` operators. The combined operator uses `_mathOperator` under the hood, so it works on points as well as numbers. Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3405
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3407
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3408
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3409
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3410
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Subscribers: vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3411
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3412
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3413
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3414
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3415
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3416
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3417
Summary: Continuation of #193 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3418
e.g. should we use the form
or
I was originally inclined to use the named arguments pattern (and have done that in many places throughout the lib); however, it looks a bit awkward when there's only one argument, especially when operators written in other libraries don't use that pattern.
Should we prefer
motionObservable.pluck('x')
ormotionObservable.pluck({ path: 'x' })
? The former is what Rx and xstream use. We originally preferred the latter, but since we started implementing our own Observable library, it's been a mix. We need to pick one strategy and use it as consistently as we can.The text was updated successfully, but these errors were encountered: