Skip to content
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

Closed
appsforartists opened this issue Mar 30, 2017 · 6 comments
Closed

Make consistent API decision about named properties #193

appsforartists opened this issue Mar 30, 2017 · 6 comments

Comments

@appsforartists
Copy link
Member

e.g. should we use the form

createProperty(342);

or

createProperty({ withInitialValue: 342 });

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') or motionObservable.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.

@appsforartists appsforartists added this to appsforartists (Brenton) in Ownership Mar 30, 2017
@appsforartists
Copy link
Member Author

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 (addedTo, subtractedBy), ones with constants (startWith, rewriteTo), ones with lists (merge, isAnyOf), and common foundational ones (_filter, _map) the named pattern is detrimental. If we stay with the positional pattern, some operators (rewrite, _reactiveNextOperator) will need to accept config objects as the last parameter, which means we shouldn't use rest parameters in our APIs.

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))

@appsforartists
Copy link
Member Author

If we go with arrays, we should be consistent there too (e.g. change the signature of merge).

appsforartists added a commit that referenced this issue Sep 20, 2017
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
@appsforartists
Copy link
Member Author

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.

@appsforartists
Copy link
Member Author

This makes sense for operators and interactions. I'll hold off on changing aggregators - seems like it would only make them less readable.

appsforartists added a commit that referenced this issue Oct 6, 2017
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
@appsforartists
Copy link
Member Author

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.

@appsforartists
Copy link
Member Author

🎊 My diff is ready for review at http://codereview.cc/D3419

appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
appsforartists added a commit that referenced this issue Oct 11, 2017
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Ownership
appsforartists (Brenton)
Development

No branches or pull requests

1 participant