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

document group & bin #1360

Merged
merged 11 commits into from
Mar 25, 2023
Merged

document group & bin #1360

merged 11 commits into from
Mar 25, 2023

Conversation

Fil
Copy link
Contributor

@Fil Fil commented Mar 20, 2023

for #1343

TODO:

@Fil Fil requested a review from mbostock March 20, 2023 10:56
@Fil Fil marked this pull request as draft March 20, 2023 10:58
@Fil
Copy link
Contributor Author

Fil commented Mar 20, 2023

I've just added an incomplete commit that shows an underlying type bug with ChannelReducers. Not sure how to solve this.

The incomplete test is in test/plots/penguin-island-mode.ts; it fails because our types aren't certain that the reduce function is called with two arguments (index, values), due to the reducer being allowed to be a ReducerFunction function(values: any[]) => any and being reused in the {reduce: T} construct of ChannelReducers.

Capture d’écran 2023-03-20 à 16 00 17

@mbostock
Copy link
Member

The incomplete test is in test/plots/penguin-island-mode.ts; it fails because our types aren't certain that the reduce function is called with two arguments

That’s only true in this branch because you changed the definition of ReducerImplementation. In the main branch TypeScript seems to infer that it’s a ReducerImplementation:

Screenshot 2023-03-20 at 10 44 31 AM

But, I do think there is ambiguity and likely a bug here introduced by #1310 (to fix #1309). Take this code:

Plot.groupX({y: "count", fill: {reduce: f}}, {x: "species", fill: "island"})

In this case, it’s hard to tell whether the fill output channel is being specified as

  1. a reducer with a scale override, in which case f is a ReducerFunction (or a ReducerImplementation object)
  2. a reducer implementation, in which case f is a ReducerImplementation["reduce"]

Currently we assume (1)

if (isObject(reduce) && typeof reduce.reduce !== "function") (scale = reduce.scale), (reduce = reduce.reduce);

and if you want a reducer implementation, you would need another level of nesting to get (2):

Plot.groupX({y: "count", fill: {reduce: {reduce: f}}}, {x: "species", fill: "island"})

but this should be considered a bug since we didn’t intend to change the interpretation. Hmm, tricky! I suppose we could look at the length of the reduce function to disambiguate but that could be brittle.

@mbostock
Copy link
Member

I wonder if we should make the map/reducer implementations much less ambiguous by using a symbol, e.g., {[Plot.reduce]: …} instead of {reduce: …} to declare an implementation. That would be possible like so:

export declare const reduce: unique symbol;

export interface ReducerImplementation {
  [reduce](index: number[], values: any[]): any;
}

It wouldn’t be backwards compatible, though… 🤔

@Fil
Copy link
Contributor Author

Fil commented Mar 21, 2023

I don't think we do (1) if f is a function. The new tests reducerBinScopeNone and reducerGroupScopeNone target this particular use case.

It seems like a bug that {reduce: ƒ, scale} is not implemented — only {reduce: "name", scale} works. I'm creating an issue (#1363).

There was a comment about the reducer label but it wasn't implemented; I fixed that and added a test, README docs and type. Maybe this would be better in a separate PR, though, since it's sort-of a new feature? (issue tracked at #1364)

@mbostock
Copy link
Member

Yes, please don’t make the label change in this documentation PR. It’s more subtle than it looks and is now delaying adding the documentation.

@Fil Fil changed the title document group document group & bin Mar 21, 2023
@Fil Fil marked this pull request as ready for review March 21, 2023 16:19
Copy link
Member

@mbostock mbostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to the Reducer types aren’t right.

A ReducerImplementation is an implementation; it can only be an object with a reduce method that takes index and values (plus a context if there’s a scope, and an extent in the case of the bin transform). A ReducerImplementation is never a named built-in reducer name, and a ReducerImplementation cannot declare a scale.

The only place where you can declare a scale override is in the context of a ChannelReducers object.

Also, the BinReducer types are specific to the bin transform, and therefore belong in src/transforms/bin; not sure why you moved them to src/reducer.

I think it’s okay to propose some small type changes along with documentation, but this is bigger than I was expecting. Changes to the types and lots of tests should be done separately from documentation. Also, I intentionally avoided documenting the reducer scope before because it’s very rarely used and adds complexity. I’m not against including scope in the types if we can figure it out, but if we can’t, I definitely think we should cut it because we already have a huge amount of work just to do the documentation, and the types are useful as-is.

@Fil
Copy link
Contributor Author

Fil commented Mar 21, 2023

Got it—I'm reverting all this. I was following my comment above, but it took me too far :)

Also this seems to work, but I wouldn't know how to express it as an interface:

export type ReducerImplementation =
  | {
      reduce(index: number[], values: any[]): any;
    }
  | {
      reduce(index: number[], values: any[], summary?: any): any;
      scope: "data" | "facet";
    };
export type BinReducerImplementation =
  | {
      reduce(index: number[], values: any[], extent: {x1: any; y1: any; x2: any; y2: any}): any;
    }
  | {
      reduce(index: number[], values: any[], summary?: any, extent?: {x1: any; y1: any; x2: any; y2: any}): any;
      scope: "data" | "facet";
    };

src/transforms/bin.d.ts Outdated Show resolved Hide resolved
@mbostock mbostock enabled auto-merge (squash) March 25, 2023 03:31
@mbostock mbostock merged commit 1decd8c into main Mar 25, 2023
@mbostock mbostock deleted the fil/ts-group branch March 25, 2023 03:34
chaichontat pushed a commit to chaichontat/plot that referenced this pull request Jan 14, 2024
* document group and bin

* edits

* allow iterable thresholds

* edits

* more type specificity

* edits

* checkpoint edits

* checkpoint edits

* edits

* fix observablehq#1384 - ChannelValueBinSpec

---------

Co-authored-by: Mike Bostock <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants