-
Notifications
You must be signed in to change notification settings - Fork 30.5k
[ramda] remove placeholder #59579
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
[ramda] remove placeholder #59579
Conversation
|
@adispring Thank you for submitting this PR! This is a live comment which I will keep updated. 1 package in this PRCode ReviewsBecause you edited one package and updated the tests (👏), I can help you merge this PR once someone else signs off on it. You can test the changes of this PR in the Playground. Status
Once every item on this list is checked, I'll ask you for permission to merge and publish the changes. InactiveThis PR has been inactive for 31 days — it is considered abandoned, and therefore closed! Diagnostic Information: What the bot saw about this PR{
"type": "info",
"now": "-",
"pr_number": 59579,
"author": "adispring",
"headCommitOid": "39117534e75ebe920bc1baa8b43b383e565a7ef1",
"mergeBaseOid": "863269c22f56d3491788a68cb70ac23ce78f3f7d",
"lastPushDate": "2022-04-08T01:09:40.000Z",
"lastActivityDate": "2022-04-18T05:31:40.000Z",
"hasMergeConflict": true,
"isFirstContribution": false,
"tooManyFiles": false,
"hugeChange": false,
"popularityLevel": "Popular",
"pkgInfo": [
{
"name": "ramda",
"kind": "edit",
"files": [
{
"path": "types/ramda/README.md",
"kind": "markdown"
},
{
"path": "types/ramda/index.d.ts",
"kind": "definition"
},
{
"path": "types/ramda/test/assoc-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/assocPath-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/concat-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/contains-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/curry-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/curryN-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/divide-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/equals-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/flip-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/groupWith-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/gt-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/gte-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/has-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/lt-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/lte-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/mathMod-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/merge-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/modulo-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/partition-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/prop-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/propOr-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/test/subtract-tests.ts",
"kind": "test"
},
{
"path": "types/ramda/tools.d.ts",
"kind": "definition"
}
],
"owners": [
"TheHandsomeCoder",
"donnut",
"mdekrey",
"sbking",
"afharo",
"teves-castro",
"hojberg",
"samsonkeung",
"angeloocana",
"raynerd",
"moshensky",
"ethanresnick",
"deftomat",
"blimusiek",
"biern",
"rayhaneh",
"rgm",
"drewwyatt",
"jottenlips",
"minitesh",
"krantisinh",
"nemo108",
"jituanlin",
"Philippe-mills",
"Saul-Mirone",
"Nicholaiii",
"LORDBABUINO",
"couzic",
"NEWESTERS",
"adispring",
"essenmitsosse"
],
"addedOwners": [],
"deletedOwners": [],
"popularityLevel": "Popular"
}
],
"reviews": [
{
"type": "stale",
"reviewer": "rbuckton",
"date": "2022-04-07T16:41:26.000Z",
"abbrOid": "768bbc5"
},
{
"type": "stale",
"reviewer": "essenmitsosse",
"date": "2022-03-31T14:19:37.000Z",
"abbrOid": "768bbc5"
}
],
"mainBotCommentID": 1082591447,
"ciResult": "pass"
} |
|
🔔 @TheHandsomeCoder @donnut @mdekrey @sbking @afharo @teves-castro @hojberg @samsonkeung @angeloocana @raynerd @moshensky @ethanresnick @deftomat @blimusiek @biern @rayhaneh @rgm @drewwyatt @jottenlips @minitesh @Krantisinh @Nemo108 @jituanlin @Philippe-mills @Saul-Mirone @Nicholaiii @LORDBABUINO @couzic @NEWESTERS @essenmitsosse — please review this PR in the next few days. Be sure to explicitly select |
|
Inspecting the JavaScript source for this package found some properties that are not in the .d.ts files. ramda (unpkg)was missing the following properties:
as well as these 5 other properties...promap, sequence, splitWhenever, unwind, whereAny |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't really speak to the removal of Placeholder since I do not use it mayself for the reasons you stated und because I find the handling just weird anyway. Don't know what the implications are for other people.
So from my side, this is good to go.
|
@adispring Unfortunately, this pull request currently has a merge conflict 😥. Please update your PR branch to be up-to-date with respect to master. Have a nice day! |
|
@essenmitsosse Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review? |
Can you give an example how the overloads with placeholder will break the type inference? |
|
(And has it been confirmed that moving placeholder overloads below normal overloads will not fix this?) |
|
((will removing the placeholder overloads be a breaking change for code that is currently using them?)) |
@ycmjason Only few ramda apis' types have Even worse, some api's Ramda and Typescript are just tools help people do their jobs more efficiently and safely, not the opposite way. For example:
Here are some more examples:
R.assoc(R.__); // Argument of type 'Placeholder' is not assignable to parameter of type 'string'.ts(2345)
R.assoc(R.__, 'value'); // Argument of type 'Placeholder' is not assignable to parameter of type 'string'.ts(2345)
R.concat(R.__)('abc'); // Expected 2 arguments, but got 1.ts(2554)
In export function includes(__: Placeholder): (list: readonly string[] | string, s: string) => boolean;
export function includes<T>(__: Placeholder): (list: readonly T[], target: T) => boolean;should be: |
|
Ramda itself may also want to remove |
|
I gave it some more thought and I agree with @adispring. I think the philosophy here should be: If you can't get it right, better leave it out. People use TypeScript to get more confidence in their code — the worst you want is a false sense of security or a feature that randomly works in some cases and in some it doesn't. It's a much clearer statement for a user to just know I'd rather have this library focus on the things it can reasonably support and do those things well. Also everybody is free to fork these types and add placeholder support and release a ramda-types-with-partial-placeholder-support version of this library. But that also reflects my use case, so I'd love to hear other peoples opinions. Additionally I think I read on the |
|
TL;DR: these aren't very convincing reasons to remove placeholder - so at the very least we should get the opinion of the community first. sourcegraph results may be useful to see how widely used placeholder is: anyway re: placeholder overloads - i don't think they're particularly difficult to add, so i strongly disagree with using it as a reason they should be removed. vaguely related - the types do not fully support ramda's api even without placeholder, since we do not support as for wrong overload implementations, this is not unique to placeholder either. so again, it should not be a reason to remove placeholder. |
|
actual tl;dr: some typings, is better than no typings (alternatively, not breaking things is better than breaking things) - so without a good reason, i would prefer not removing them |
|
TypeScript is just a static tools, I don't think this breaking change will introduce much destructive effect, it will effect nothing in run time. People can easily replace |
I've just seen these 274 use case of
|
|
@adispring The CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! Note: builds which are failing do not end up on the list of PRs for the DT maintainers to review. |
|
@adispring The CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! Note: builds which are failing do not end up on the list of PRs for the DT maintainers to review. |
There are no immediate plans for removing the placeholder. There is a certain distaste for it among the core team, and I think many of us would be happy to see it go. But there is no obvious replacement for this without a radical change to the library. That may happen one day, but it's not in the plans for 1.0 or 2.0. I could see us disallowing the placeholder being passed as the last argument in any one call. It makes no logical sense, and probably contributes to the difficulties typing functions here. It's allowed because there was no reason to add complexity to the code in order to disallow it; and if it's allowed, there is no reasonable alternative but to treat it as a no-op. But if this is a significant factor in making it hard to type Ramda, we could definitely at least fix that. I really don't know TS well, and am not versed in the details of the typings files. But would disallowing that make typing Ramda's more complex signatures more palatable? Ramda has always been a JS library, and we haven't really even tried making concessions in order to work better with TS. We probably still wouldn't do so for our core API design, but this is a minor implementation detail. It would technically be a backward incompatibility, but as we (finally) approach 1.0, we're going to have a few of those, so this might be a good time to talk about it. We could also do something to the actual value, if it helps. It's an object with a special named property on it; that's it. If it would help for it to be of some constructed type, we could probably do so, so long as we can keep that property. |
|
@rbuckton, @essenmitsosse Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review? |
|
@CrossEye thanks for the input, that is definitely helpful. Regarding disallowing certain use cases for Placeholders in the JavaScript implementation: It say it doesn't really matter for the Typings. If we got the input that certain use cases aren't really intended or necessary, its perfectly fine to not support them in TypeScript, while they still might be valid when actually running the code. Example: const add = (a, b) => a + bAssuming this function is supposed to add numbers, its implementation would allow it to also concatenate strings. So when writing a JavaScript library you might want to add a run time check to avoid someone foot-gunning themself. But in TypeScript, you could type this function as only being allowed to take a number — making it actually easier to narrow the use case, without the need for runtime checks. To me, that sounds like your "There is no point in allowing What makes Placeholders so tricky to type is: You need to either a) will inevitably miss some use case and will still be quite a lot of work. It will also be super hard to maintain. b) seems to be the easier approach, but it will sooner or later reach its limitation — at the latest when generics come into play (probably making you have to fallback on a)). It most likely will also make the resulting types super hard to read — probably for everybody, even people who never want to use Placeholders. My assessment would be that adding full Placeholder support would most definitely be fantastic for people who actually use Placeholders and TypeScript — if we ever get it to fully work and not fail halfway through. But it will be an immense amount of work on the whole library (just for one feature) and make maintaining much harder in the future. For me this brings us back to the question: is it really worth it? All the work put into supporting Placeholders, will not only not at all benefit people who don't use. It will probably make the library more error prone and less well maintained and up to date, then if we just dropped support. I agree with @rbuckton, that we should look at the ramifications, of dropping support for Placeholder. But we should also look at the ramifications of continuing support for it or even expanding it. Right now it looks like, Placeholder is of little use, when used with TypeScript due to how little support there is as of now. Maybe someone could show some examples of how function types would change with full Placeholder support? |
I can't do that; I don't have the necessary TS skills. But it will multiply the number of signatures greatly, even if Ramda disallows a final placeholder argument. If it doesn't, I'm not sure what can be done, as this is perfectly legal in Ramda: foo (arg1) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (__) (arg2)
// `--------------------------------------- n times ----------------------------------------'for any non-negative integer, So if foo (a, b, c)
foo (a, b) (c)
foo (a) (b, c)
foo (a) (b) (c)
foo (__, b) (a, c)
foo (__, b) (a) (c)
foo (__, b) (__, c) (a)
foo (__, b, c) (a)
foo (__, __, c) (a, b)
foo (__, __, c) (a) (b)
foo (__, __, c) (__, b) (a)
foo (a, __, c) (b)(and I'm likely missing some.) This does not include the stupid calls such as Am I correct that even if a signature is not specified in the typings file, the user can overwrite the type of any given expression as needed? So if the Ramda-supported type was not supplied by TS, the user could still describe the type manually? |
|
I actually played around a little bit, and it seems with recursive types implementing Placeholder would actually be possible — including all the weird edge cases. Downsides are:
|
|
If we wouldn't have to worry about generics you could type it like that, which would result in quite decent types: But there are barely Ramda functions that wouldn't need generics. :/ |
|
Worth noting that while it is legal - is it a case that Ramda wants to support? As for overwriting the type - yes, I think it is possible: Not to mention there is always the type assertion as the last resort - something like: const add: {
(my: Overload): 1;
(my: Overload): 2;
} = R.add as any; |
|
@adispring Unfortunately, this pull request currently has a merge conflict 😥. Please update your PR branch to be up-to-date with respect to master. Have a nice day! |
We could change this behavior, while it is intentional, it is mostly just there for logical compatibility and not for practical work. I would also have no objection to having TS reject it even if underneath Ramda still allows it. The reasoning is simple enough for empty argument lists returning a function equivalent to the one that was called. If we start with, say, four formal parameters, and we supply four of them, we're done and we call the function. If we supply three of them, we get back a function expecting the remaining one. If we supply two, we get back a function expecting the remaining two. If we supply one, we get back a function expecting the remaining three. So it seems eminently logical that if we supply zero, we get back a function expecting all four. For placeholder, it's a very similar case. Again, with four formal parameters, if we supply non-placeholder values at indices This feels much like the fact that The big problem for Ramda if we were to change this is that our only alternative here would likely be to throw an exception, and Ramda, whose main focus is on composability, is very loathe to throw. But we can work that out. I wouldn't bother, though, if this is something that TS can handle without crazy gymnastics. |
|
TL;DR - sorry, bad wording - I meant more along the lines of "is this an intended usecase of Ramda" rather than "maybe Ramda should go out of its way to disallow it". Not specialcasing it in the implementation definitely makes things a lot easier to reason about, among other things IMO it's less about "the types should reflect the semantics of Ramda 1:1" and more "the types should reflect Ramda's intended usecases" - Or in other words, the way I see it, TypeScript's intention is not to be a type system so much as it is meant to be a tool to help in writing correct code |
|
Note though, I think to support placeholder well, we'd need to add a huge number of new interfaces (for all possible combinations of placeholders and filled-in parameters) - I'm not sure if it's possible to make a helper type but I wouldn't be optimistic because said helper type would have to work correctly with generics So the question is, is the relatively low fraction (not sure how to estimate actually) of users using placeholder, worth making the types several times bigger (and potentially less ergonomic) |
|
Is there some well-respected introduction to TS typings files? I think I'm still missing a lot of the big picture. I'm not sure how much time and energy I want to invest in learning about them, but if I do choose to put some real effort into it, what's a good place to start? I've seen plenty of the TS for JS developers articles and the I never thought I'd switch, but now I'm so happy ones, and none have really struck any chords for me. If I'm going to dig in, it's probably from this angle. How can we handle JS's trickier signatures? |
|
I think the handbook is what's generally recommended - however note that the other sections (on TypeScript in general) are probably useful too - for the most part this is TypeScript definitions, anything specific to typings files that may have needed to be done has already been done. Supposedly it takes 30 minutes to go through, although I suspect it'd take quite a bit longer to absorb - and even longer still to play around with specific typings(/constructs) to get a feel of the strengths and weaknesses of each |
|
@adispring I haven't seen any activity on this PR in more than three weeks, and it still has problems that prevent it from being merged. The PR will be closed on May 18th (in a week) if the issues aren't addressed. |
|
@adispring To keep things tidy, we have to close PRs that aren't mergeable and don't have activity in the last month. No worries, though — please open a new PR if you'd like to continue with this change. Thank you! |
Ramda does not support TS well, one of the reason is TS does not support curry well enough, and
R.placeholdermake this worse, such as put a function with R.placeholder as a param into another function, in this case TS can not infer the type correct.R.merge and R.contains have been removed since v0.28.0.
Please fill in this template.
npm test <package to test>.Select one of these and delete the others:
If adding a new definition:
.d.tsfiles generated via--declarationdts-gen --dt, not by basing it on an existing project.tslint.jsonshould contain{ "extends": "@definitelytyped/dtslint/dt.json" }, and no additional rules.tsconfig.jsonshould havenoImplicitAny,noImplicitThis,strictNullChecks, andstrictFunctionTypesset totrue.If changing an existing definition:
If removing a declaration:
notNeededPackages.json.