panzerdp / voca

The ultimate JavaScript string library
https://vocajs.pages.dev
MIT License
3.6k stars 136 forks source link

Reverse argument order #4

Open tomhicks-bsf opened 7 years ago

tomhicks-bsf commented 7 years ago

This is quite a big suggestion, but in my opinion the order of the arguments is inconsistent across functions, and generally the wrong way around.

See these for details: http://functionaltalks.org/2013/05/27/brian-lonsdorf-hey-underscore-youre-doing-it-wrong/ https://jsleao.wordpress.com/2015/02/22/curry-and-compose-why-you-should-be-using-something-like-ramda-in-your-code/

There are some cases where API is the right way around, and would allow for currying or partial application:

v.sprintf('%s costs $%.2f', 'Tea', 1.5);

Here, you take config first, data last, which is the "right way". This allows you to things like:

const logMyStuff = v.sprintf.bind(null, "%s costs $%.2f")
logMyStuff("Tea", 1.5)
logMyStuff("Coffee", 2.40)

which allows you to use this function in compose chains, for example.

You cannot do that with this function:

v.replaceAll('good morning', 'o', '*');

If the order were reversed, and currying implemented, you could do this:

const changeOsToStars = v.replaceAll("o", "*")

And now you have a function you can use over and over.

With the use of FP (and in particular partial application and currying) on the rise in JavaScript, I think this library should take a leaf from ramda's book and put config first, data last, and curry by default.

Thoughts?

panzerdp commented 7 years ago

Hey @tomhicks-bsf,

Thanks for your nice suggestions!

The library is implementing a standard and the most expected behavior to put the subject of the operation as the first argument. It's clearly the same way as lodash, underscore and other libraries offer. At this level the reordering of function arguments is not a change that I could implement.

The idea about partial application and currying is interesting. In many (but not the most) situations it brings useful benefits of reusing the configured functions (like you mention logMyStuff).
For such case a flexible solution might be to provide a set of separated functions wrapped in a way to produce immutable auto-curried iteratee-config-first data-last methods.

I like the way lodash implemented that: see more details.

Rough it may work this way:

const v = require('voca/fp');
//
const replaceAll = require('voca/fp/replaceAll');
const changeOsToStars = replaceAll("o", "*");

Such task requires quite a lot of work. But I will think about a plan.

Jacse commented 7 years ago

Voca is in many ways an obvious candidate to auto-curried, data-last, composable-functions. With that said, I (unfortunately) think the current implementation is what seems natural for the majority of javascript developers.

Following the lodash way is probably the best way.

Jacse commented 7 years ago

Maybe functions could be FP-altered in src/functions.js. Like:

// Helper func for reversing a binary function
flip = func => args => func(args[1], args[0];

[...]

export default {
    charAt: flip(charAt),
    codePointAt: flip(codePointAt)
}

Same principle with currying. But I'm not sure if this is even an acceptable way, I haven't looked through the source code properly.

Question: What would happen to functions with optional parameters? To support auto-currying they would have to be fixed to a specific arity, so would that just mean no optional parameters in the fp functions?

panzerdp commented 7 years ago

Hey @Jacse, Thanks for providing your ideas. I agree, the changes can be at the src/functions.js level. I need some time to design what exact modifications are necessary and how the final FP API of Voca will look. And yes, the FP functions may be fixed to a specific arity.

Jacse commented 7 years ago

@panzerdp sounds fantastic - good luck 🍀

briancodes commented 5 years ago

Maybe functions could be FP-altered in src/functions.js. Like:

// Helper func for reversing a binary function
flip = func => args => func(args[1], args[0];

It might be easier to use a more general flip function that works for any arity like const flip = fn => (...args) => fn.apply(null, args.reverse());