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

Support for curried functions #1286

Open
akkie opened this issue Oct 21, 2016 · 25 comments

Comments

@akkie
Copy link

commented Oct 21, 2016

I've not found any documentation about how curried functions should be documented. Lets asume we have the following function:

const push = browserHistory => route => browserHistory.push(route)

Is this a valid documentation?

/**
 * @param {Object} browserHistory 
 * @param {string} route
 */
const push = browserHistory => route => browserHistory.push(route)
@kurtmilam

This comment has been minimized.

Copy link

commented Nov 24, 2016

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.

@kurtmilam

This comment has been minimized.

Copy link

commented Nov 24, 2016

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.

@minexew

This comment has been minimized.

Copy link

commented Dec 2, 2016

Do you actually use curried functions in real-world code?

@kurtmilam

This comment has been minimized.

Copy link

commented Dec 6, 2016

@minexew Yes, I do actually use curried functions and partial application in real-world code.

FYI:

  • lodash, one of the most dependend-upon libraries in npm, has partial and curry methods.
  • Underscore has a partial method.
  • Native JavaScript has Function.prototype.bind(), which can be used for partial application.
  • JQuery has proxy, which can be used for partial application.

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

@minexew

This comment has been minimized.

Copy link

commented Dec 6, 2016

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.

@kurtmilam

This comment has been minimized.

Copy link

commented Dec 6, 2016

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

This comment has been minimized.

Copy link

commented Apr 26, 2017

Does anyone actually have a solution for this instead of philosophical debates about the need of currying?

@jsatk

This comment has been minimized.

Copy link

commented Apr 28, 2017

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.

@karol-majewski

This comment has been minimized.

Copy link

commented Jun 16, 2017

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 Parameter 'route' implicitly has 'any' type. message, you'd have to use inline types:

const push = browserHistory => (/** @type {string} */ route) => browserHistory.push(route);
@dietergeerts

This comment has been minimized.

Copy link

commented Sep 6, 2017

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:

/**
 * Create model searcher
 *
 * As url, give the base one, query params will be appended automatically.
 *
 * @param {string} url - Base url template string with `context` as param
 * @param {Object.<number, string>} errorKeys - Like {httpStatus: translationKey}
 * @param {function(DTO): OBJ} deserializer
 * @param {$http} $http
 * @param {translate} translate
 * @returns {function(*): ModelSearcher}
 * @template DTO, OBJ
 */
export function modelSearcher(url, errorKeys, deserializer, $http, translate) {

    /**
     * @callback ModelSearcher
     * @param {Object} criteria
     * @returns Rx.Observable.<OBJ[]>
     * @template CRITERIA
     */

    return context => criteria => Rx.Observable
        .fromPromise($http.get(`${template(url)({context})}${queryStringFrom(criteria)}`))
        .catch(throwRxError(translate(errorKeys[500])))
        .map(getResponseData(deserializer));
}
@angeloocana

This comment has been minimized.

Copy link

commented Oct 12, 2017

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.

@asistapl

This comment has been minimized.

Copy link

commented Nov 4, 2017

@karol-majewski This solution provides best IntelliSense in VS Code:

curriedfun

@karol-majewski

This comment has been minimized.

Copy link

commented Nov 4, 2017

@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);
skylerlee added a commit to radonlab/predator-web that referenced this issue Nov 25, 2017
Add docs
jsdoc not support curry function, ref:
jsdoc/jsdoc#1286
@eyalcohen4

This comment has been minimized.

Copy link

commented Dec 26, 2017

Would love to help with this one. arrow functions support will be a great feature 😿

@lll000111

This comment has been minimized.

Copy link

commented Jan 31, 2018

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

/**
 * @param {MyTape} firstParam
 * @returns {function(function(SHA256Hash):boolean):function(SHA256Hash):Promise.<string>}
 */

which is a monster line (function that returns a function that returns a function). also, the @param only is for the outermost parameter of the very first function. All others are declared in the @return tag, which gets very crowded.

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

@babakness

This comment has been minimized.

Copy link

commented Feb 28, 2018

Really look forward to seeing this happen!

@dietergeerts

This comment has been minimized.

Copy link

commented Apr 3, 2018

@minexew

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.

Because the rest of the code uses the function in different ways? like:

const doSomethingWith = _curry((configParam, dataParam) => {});
const doSomethingWithConfigA = doSomethingWith('lala');

doSomethingWithConfigA(5);
doSomethingWith('hey', 9);

The difficult part of this is adding JSDoc for doSomethingWith...

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.

@nfantone

This comment has been minimized.

Copy link

commented Jul 4, 2018

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 param and returns metadata are being stripped from generated docs. Any ideas if this is even possible currently?

@dietergeerts

This comment has been minimized.

Copy link

commented Jul 5, 2018

@nfantone , it does work, but you have to add @function in the docblock

@nfantone

This comment has been minimized.

Copy link

commented Jul 5, 2018

@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.

@OmgImAlexis OmgImAlexis referenced this issue Sep 18, 2018
10 of 10 tasks complete
@JesterXL

This comment has been minimized.

Copy link

commented Oct 3, 2018

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 async keyword, etc). For those who love types, I feel for you.

So I built a Hindley-Milner documentation library where you can use Markdown. Hopefully it helps y'all.

https://github.com/JesterXL/hm-doc

@joelnet

This comment has been minimized.

Copy link

commented Oct 10, 2018

@JesterXL The issue has only been open since October 2016. Be patient!

@lll000111

This comment has been minimized.

Copy link

commented Oct 10, 2018

@joelnet We get what we pay for...

@dietergeerts

This comment has been minimized.

Copy link

commented Oct 15, 2018

@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:

@curried
@function
@param {string} a
@param {string} b
@returns {string}

export const test = _curry((a, b) => a + b);

My IDE (WebStorm) is fine with this + it ends up in the API docs.

@phillt

This comment has been minimized.

Copy link

commented Nov 19, 2018

Do you actually use curried functions in real-world code?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.