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

Various small fixes #1

Merged
merged 2 commits into from
Jul 5, 2019
Merged

Various small fixes #1

merged 2 commits into from
Jul 5, 2019

Conversation

OliverJAsh
Copy link
Member

@@ -18,6 +18,7 @@ const it = (name: string, fn: () => void) => {
describe('pipe', () => {
it('works when first function has single param', () => {
// One test for each overload
assert.strictEqual(pipe(singleParamFnAdd1)(1), 2);
Copy link
Member Author

Choose a reason for hiding this comment

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

Something for the future: test the type output

Copy link
Contributor

Choose a reason for hiding this comment

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

Might be something we could take a stab at as @WrocTypeScript/organisers for the purpose of doing WrocTypeScript/talks#19.

I'm sure many have the same problem.

"devDependencies": {
"@types/node": "^12.0.10",
Copy link
Member Author

Choose a reason for hiding this comment

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

Only used by tests, so safe to move here

Copy link
Contributor

Choose a reason for hiding this comment

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

Precisely. Reference tweet for anyone interested:

https://twitter.com/bterlson/status/1100797304632557573

@OliverJAsh OliverJAsh requested a review from karol-majewski July 1, 2019 13:51
@OliverJAsh OliverJAsh assigned OliverJAsh and unassigned OliverJAsh Jul 1, 2019
@OliverJAsh OliverJAsh marked this pull request as ready for review July 1, 2019 13:52
@@ -55,6 +55,10 @@ const result: number = pipeWith(1, add1, times2);
assert.strictEqual(result, 4);
```

## Note about type safety

`pipe` and `pipeWith` currently support up to 9 functions. If more than 9 functions are passed, type safety will be lost. If need be, we are open to adding more overloads to avoid this.
Copy link
Member Author

Choose a reason for hiding this comment

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

This is why I initially skipped adding an overload for "any number of functions", because it loses type safety. 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

Which is the correct way to go. My concern was not about the upper limit (9+) but the lower one.

pipe and pipeWith should be allowed to accept parameters dynamically, and as such, they should work with an empty list of arguments or with a single function passed as a tuple.

The original motivating example:

declare const functions: [add1] | [add1, multiplyBy2] | [add1, multiplyBy2, toString];

pipeWith(
  0,
  ...functions, // Unexpected compile-time error
)

Copy link
Contributor

Choose a reason for hiding this comment

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

We could also suggest adding overloads locally in the README if one really needs them to keep the core small and maintainable.

declare module 'pipe-ts' {
  export function pipeWith<A, B, C, D, E, F, G, H, I, J, K>( /* ... */);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

My concern was not about the upper limit (9+) but the lower one.

Are you saying we don't need the 10+ overload? I think we do, to support the array use case?

Copy link
Contributor

Choose a reason for hiding this comment

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

We could run a BigTSQuery to see how popular this pattern is (strongly typed tuples) or see how many overloads others libraries are offering and do the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

compose from Redux bails out after 4 functions:

https://github.com/reduxjs/redux/blob/d4075e111612a7bfb5a1fbeabc219ae81c4eb099/index.d.ts#L549-L578

Sanctuary can manage 5 functions:

https://github.com/karol-majewski/DefinitelyTyped/blob/5f59f3dc7a0965fb1767cf13c670c633bbbbf0a8/types/sanctuary/index.d.ts#L184-L189

Ramda does 10 functions:

https://github.com/karol-majewski/DefinitelyTyped/blob/5f59f3dc7a0965fb1767cf13c670c633bbbbf0a8/types/ramda/index.d.ts#L2093-L2103

I think what 10 should be enough + we need a bailout strategy just like Redux does with compose (although they make poor use of type parameters here).

export function compose<R>(
  f1: (b: any) => R,
  ...funcs: Function[]
): (...args: any[]) => R

Copy link
Member Author

Choose a reason for hiding this comment

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

My worry about having a "bailout" array overload is that type safety will silently disappear once you get past a certain threshold, e.g. I have a pipe with 9 functions, I add another one, and now suddenly I'm into any/unknown territory. I guess that's OK as long as we use unknown and not any (as I currently am in this PR)?

Or would it be better to do away with this bailout overload? If we did that, would the use cases you're concerned about (tuples up to 9 items in length) still work?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess that's OK as long as we use unknown and not any (as I currently am in this PR)?

Exactly!

Or would it be better to do away with this bailout overload?

We need to draw the line somewhere.

  • If we decide not to add the bailout one at all, it won't compile for people using more than 10. They will need to assert both their arguments (because there will be no matching overload) and the final return type.
  • If we do add it, the consumers will need to assert the return type (because they will be given unknown).

This might look stupid, but if we are to allow the operator functions to be passed dynamically, then these two would be justified:

No functions

export declare function pipeWith<A>(a: A): A;

A lot of functions

export declare function pipeWith(a: any, ...functions: Function[]): unknown;

With these in place, it works like this:

const add = (x: number) =>
  (y: number) =>
    x + y;

const add5 = add(5);

const zero = [] as const;
const one = [add5] as const;
const five = [add5, add5, add5, add5, add5] as const;
const ten = [add5, add5, add5, add5, add5, add5, add5, add5, add5, add5] as const;
const twelve = [add5, add5, add5, add5, add5, add5, add5, add5, add5, add5, add5, add5] as const;

pipeWith(0, ...zero);   // $ExpectType 0
pipeWith(0, ...one);    // $ExpectType number
pipeWith(0, ...five);   // $ExpectType number
pipeWith(0, ...ten);    // $ExpectType unknown
pipeWith(0, ...twelve); // $ExpectType unknown

Copy link
Member Author

@OliverJAsh OliverJAsh Jul 5, 2019

Choose a reason for hiding this comment

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

  • If we decide not to add the bailout one at all, it won't compile for people using more than 10. They will need to assert both their arguments (because there will be no matching overload) and the final return type.

Or:

  • Add an overload locally
  • Send a PR to add an overload upstream
  • Nest pipes! - pipe(...8Fns, pipe(...another9Fns)) 😆

Also worth mentioning that I've never had a tuple of functions longer than 9 before, and I've used pipe for a long time :-P

FWIW I had to remove the bailout/array overload due to a TS bug: #3. So we can continue this discussion but keep in mind that right now it's a non-option!

Copy link
Contributor

Choose a reason for hiding this comment

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

Also worth mentioning that I've never had a tuple of functions longer than 9 before, and I've used pipe for a long time :-P

Exactly! I don't think it's much of an issue, and if it is, then the users will tell us about it.

Copy link
Contributor

@karol-majewski karol-majewski left a comment

Choose a reason for hiding this comment

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

👍

@OliverJAsh OliverJAsh merged commit 81228d8 into master Jul 5, 2019
@OliverJAsh OliverJAsh deleted the fix/various branch July 5, 2019 13:31
This was referenced Jul 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants