-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Support for curried functions #1286
Comments
I'm looking for the same thing. One option would be something like the following: /**
* @param {Object} browserHistory
* @returns {function} a function accepting a single route parameter
* @param {string} route
* @returns {boolean} whatever browserHistory.push(route) returns
*/
const push = browserHistory => route => browserHistory.push(route) But I'd like to know whether there's an official way to do it. |
My IDE complains about the previous option, but seems OK with this one: /**
* @param {Object} browserHistory - thing
* @returns {function} a function that accepts a single route parameter
*/
const push = browserHistory =>
/**
* @param {string} route - thing
* @returns {boolean} whatever browserHistory.push(route) returns
*/
route => browserHistory.push(route) Ugly, though. |
Do you actually use curried functions in real-world code? |
@minexew Yes, I do actually use curried functions and partial application in real-world code. FYI:
I prefer Ramda and friends for my functional programming in JavaScript needs at the moment, but the new ES6 arrow functions also make it quite easy to brew your own curried functions and/or partial application in a nice, terse syntax (see previous posts in this discussion for an example). |
All of those examples take in a "normal" function and currify it (or perform partial application). That's indeed often useful. What I don't comprehend why anybody would want to make a function curried by default, in a way that doesn't allow supplying more than one argument per call. |
For the same reason they'd want to curry an existing function. It makes more sense when you get deeper into functional programming. Furthermore, some libraries (Ramda is one) offer a hybrid style of currying that lets you pass in one or several arguments at a time, and only executes the original function after all of the arguments it needs have been supplied (for non-variadic functions with an arity <= 10). The function in the original post in this discussion is curried in a manner that requires one to call it twice, passing in one argument each time. With Ramda-style currying, you be able to choose between passing in both arguments in one call or each argument in its own separate call. Also, using the example function in the first post, if you're usually passing one parameter at a time to your functions, I don't see the problem with writing a function that is curried by default and calling it like this when you want/need to pass both arguments at the same time: push( history )( rt ) |
Does anyone actually have a solution for this instead of philosophical debates about the need of currying? |
Agreed. Would love a solution for how to document this. |
This one makes Visual Studio Code provide proper IntelliSense: /**
* @param {Object} browserHistory
* @returns {function(string): boolean} a function accepting a single route parameter
*/
const push = browserHistory => route => browserHistory.push(route); However, if you're using JSDoc annotations to leverage type checking, it won't do. To deal with TypeScript and its const push = browserHistory => (/** @type {string} */ route) => browserHistory.push(route); |
Any news on this? Functional programming rocks in JS, makes everything more readable, understandable and testable, but the docs have to be ok too... tried the following, but then IntelliJ doesn't help me anymore on the returned result:
|
I'm looking for a solution to Ramda curry, I love it, but js docs is bad =/ Example: import { curry, filter, startsWith } from 'ramda';
/**
* Get valid langKey in langs or return defaultLangKey
* @func
* @param {[String]} langs allowed lang keys ['en', 'fr', 'pt']
* @param {String} defaultLangKey default browser language key
* @returns {String} valid langKey
*/
const getValidLangKey = curry((langs, defaultLangKey, langKey) => {
const currentLangKey = filter(l => startsWith(l, langKey), langs);
return currentLangKey[0] || defaultLangKey;
});
export default getValidLangKey; After adding curry() I lost all the types and descriptions. I hope someone finds a good solution. |
@karol-majewski This solution provides best IntelliSense in VS Code: |
@asistapl That's correct, support for JSDoc has gotten better over last year. I believe we can apply your solution to the original problem like this: // @ts-check
/**
* @typedef {object} BrowserHistory
* @property {(route: string) => void} push
*/
/**
* @param {BrowserHistory} history
* @returns {(route: string) => void}
*/
const push = history => route => history.push(route); |
Would love to help with this one. arrow functions support will be a great feature 😿 |
Support in one IDE is one thing, I would be interested in support for the HTML output — in a way that makes the intent clear. For WebStorm I just wrote
which is a monster line (function that returns a function that returns a function). also, the The examples above look okay because there is only one level. It should work with 5 levels too (just to give a high(er) number). |
This comment was marked as spam.
This comment was marked as spam.
Because the rest of the code uses the function in different ways? like:
The difficult part of this is adding JSDoc for In functional programming, it is often done to config your function first with the first x params, and then execute it with the rest, but that's not always the case. |
What about point-free functions that don't have explicit arguments declaration? const { add } = require('ramda');
/**
* Adds 10 to `x`.
* @param {number} x Any number
* @returns {number} The given `x` number plus 10.
*/
const sum10 = add(10); ☝️ This, unfortunately, doesn't work out of the box. The |
@nfantone , it does work, but you have to add |
@dietergeerts Worked like a charm. Thanks a million for pointing it out! Still kinda awkward, if you ask me. I get the idea of wanting to infer types or match written docs with actual params to lint/give feedback on potentially bad tags - but removing them entirely from the output, by default, seems a bit forced, honestly. |
Y'all are wonderful, but I gave up trying to figure out how to get all these workarounds to work (insane nesting, jsdoc or babylon failing to recognize the So I built a Hindley-Milner documentation library where you can use Markdown. Hopefully it helps y'all. |
@JesterXL The issue has only been open since October 2016. Be patient! |
@joelnet We get what we pay for... |
@JesterXL , like @joelnet says, be patient. Also keep in mind that there are features coming in JS regarding currying and partial application, which could change the implementation of this. (https://github.com/tc39/proposal-partial-application) For what it's worth, I use currying a lot, through the lodash helper, and I document such functions as:
export const test = _curry((a, b) => a + b); My IDE (WebStorm) is fine with this + it ends up in the API docs. |
Yep, I use them to create higher ordered components in React, and other various libraries for React use them including ReactRouter ReactRedux just to name a few. |
Yes, I do. |
Still not possible to use JDoc on curried function? |
@BenjaminBrodwolf Well - nitpicking here, but that's not a curried function. That's a couple of function returning functions. So the "natural" way to jsdoc it, IMHO, would be: /**
* Expects an argument and returns a function.
* @param {*} x First argument.
* @returns {Function} A function that expects an argument `y` and returns a new
* function that asks for a curried function `f` and invokes it with both `x` and `y`.
*/
const pair = x => y => f => f(x)(y); Or you could document each returned function individually. |
@nfantone
That's the definition of true currying. Here, from wikipedia (emphasis mine):
You may be thinking of Ramda-style currying, which I've seen Ramda maintainers refer to as 'smart currying' to differentiate it from the actual currying @BenjaminBrodwolf 's code exemplifies. Unfortunately, AFAIK, it's still not very convenient to document Ramda 'smart currying' OR true currying via jsdoc. To be clear, I think 'classical' currying and 'smart' currying are both useful. I'd like to see support for documenting both flavors improved, and I think it's OK to use 'currying' in this discussion to refer to either or both styles. |
@kurtmilam Fair enough. Thanks for the clarification. However, I stand by what I said. I was not actually thinking about Ramda or what brought up as "smart currying". Here, let me explain. When we say that So, in the context of JavaScript as a language, |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
On the topic, and since Ramda was mentioned before, an approach I usually take to document curried functions with jsdoc is to place the docs in the original/regular JS function and export the result of const { curry, pathSatisfies } = require('ramda');
/**
* Checks whether the given predicate is satisfied for the property value
* at `propName` under `lambdaEvent.requestContext.authorizer`.
*
* @function
* @see https://ramdajs.com/docs/#pathSatisfies
* @param {Function} predicate The boolean returning function to apply to the
* value of the `propName` property.
* @param {String} propName The name of the property to evaluate.
* @returns {Boolean} The result of invoking `predicate` with the value of the
* property named `propName` on the `lambdaEvent.requestContext.authorizer` object.
*/
const authorizerPropSatisfies = curry(function authorizerPropSatisfies(predicate, propName) {
return pathSatisfies(predicate, ['lambdaEvent', 'requestContext', 'authorizer', propName]);
});
module.exports = authorizerPropSatisfies; |
The name you’re looking for is high order functions, not currying. Just my two cents… |
Then the issue should be submitted to that team, no? These are the issues for the documentation generator. First sentence from the README for this repo right here is:
If someone wants to work something with tool X I think it's better to ask the guys responsible for tool X. The author of this repo here can do nothing at all for VScode or whatever other IDE or tool. |
Of course! |
It seems that this syntax: /**
* @param {BrowserHistory} history
* @returns {(route: string) => void}
*/
const push = history => route => history.push(route); Does not work well for JSDoc to Markdown generators like jsdoc-to-markdown and markdox. These tools cannot parse these Did anyone experience the same and come with a workaround when using such tools? How did you end up documenting your callbacks and thunks (higher-order function's function return value)? |
It does work! Have you tried enabling |
@nfantone How do you enable |
@tonix-tuft You can either add |
there's still a discussion about if it worth doing this or it was decided that it should be supporting currying definitions? |
Unfortunately, the solution above shows all parameters to be of type
This is the only solution that worked for me: const { curry, pathSatisfies } = require('ramda');
/**
* Checks whether the given predicate is satisfied for the property value
* at `propName` under `lambdaEvent.requestContext.authorizer`.
*
* @function
* @see https://ramdajs.com/docs/#pathSatisfies
* @param {Function} predicate The boolean returning function to apply to the
* value of the `propName` property.
* @param {String} propName The name of the property to evaluate.
* @returns {Boolean} The result of invoking `predicate` with the value of the
* property named `propName` on the `lambdaEvent.requestContext.authorizer` object.
*/
const authorizerPropSatisfies = curry(
/** @type {(predicate: function, propName: string) => boolean} */
(predicate, propName) => pathSatisfies(predicate, ['lambdaEvent', 'requestContext', 'authorizer', propName]),
);
module.exports = authorizerPropSatisfies; The tradeoff: You have to declare types twice, once inside jsdoc and once just above the function declaration (and inside However, this doesn't feel completely unnatural as it resembles |
@simonwjackson Ah, yes. You're absolutely right - I did not typed the arguments in my original example for the inner function. From your suggestion, I noted that if you omit the type definitions on the top function, vscode would infer them just fine nonetheless. /**
* Checks whether the given predicate is satisfied for the property value
* at `propName` under `lambdaEvent.requestContext.authorizer`.
*
* @function
* @see https://ramdajs.com/docs/#pathSatisfies
* @param predicate The boolean returning function to apply to the
* value of the `propName` property.
* @param propName The name of the property to evaluate.
* @returns The result of invoking `predicate` with the value of the
* property named `propName` on the `lambdaEvent.requestContext.authorizer` object.
*/
const authorizerPropSatisfies = curry(
/** @type {(predicate: (value: any) => boolean, propName: string) => (obj: Object) => boolean} */
(predicate, propName) =>
pathSatisfies(predicate, ['lambdaEvent', 'requestContext', 'authorizer', propName])
); This way, there's no type duplication - at the probable expense of some reading clarity. I also improved some of the definitions so they are closer to their |
@nfantone This is great! I wonder if any of the documentation generators (TypeDoc) will pick up the types as well. |
@simonwjackson Good question. Would have to try that - although my hunch says they won't. Let me know if you do! |
This solution worked for me with Vim, CoC and coc-tsserver. Intellisense and parameter hints working perfectly. I'm very grateful! Thanks. |
Just wanted to share a
The main downside of this approach is that you will only see the callback type intellisense once you're inside of the actual parameter list, that info is missing when viewed in the finder list: Keep in mind that Here's a full and practical example with that fix applied:
Note how I'm also manually providing |
After reading jsdoc/jsdoc#1286
I've not found any documentation about how curried functions should be documented. Lets asume we have the following function:
Is this a valid documentation?
The text was updated successfully, but these errors were encountered: