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

Curry placeholder should play nice with other Ramda's and other libs #1052

Closed
paldepind opened this issue Apr 26, 2015 · 24 comments
Closed

Curry placeholder should play nice with other Ramda's and other libs #1052

paldepind opened this issue Apr 26, 2015 · 24 comments

Comments

@paldepind
Copy link
Member

Currently the Ramda __-placeholder is defined like this:

module.exports = {ramda: 'placeholder'};

And it is checked for at various places with strict equality:

if (arguments[idx] === __) {
  // foo
}

Now, consider what happens if I'm using npm to manage dependencies and I'm using a library, someFnLibDepOnOldRamda, that depends on a slightly different version of Ramda than the one I'm using myself. Then npm will pull in two different versions of Ramda and with them two different instances of __. Thus the strict equality check that Ramda uses breaks.

If pass my instance of __ to a function from the library, curried with it's Ramda's curry it will not be recognized as a placeholder. Bam! I now have an obscure confusing bug :(

One solution: Define this placeholder like this:

module.exports = {someSpecialPropertyName: true};

And check for it like this:

function isPlaceholder(o) {
  return o && o.someSpecialPropertyName === true;
}

In particular the name someSpecialPropertyName should not include the word "ramda". This would make it more natural for potential other curry implementations to adopt the same placeholder "specification".

@paldepind paldepind changed the title Curry placeholder sould play nice other Ramda's and other libs Curry placeholder should play nice other Ramda's and other libs Apr 26, 2015
@paldepind paldepind changed the title Curry placeholder should play nice other Ramda's and other libs Curry placeholder should play nice with other Ramda's and other libs Apr 26, 2015
@paldepind
Copy link
Member Author

It was apparently quite hard for me to get the title right :(

@buzzdecafe
Copy link
Member

perhaps we should use a Symbol and do as you suggest for environments that don't support Symbol

@CrossEye
Copy link
Member

🌿

I like this idea a lot. For some time our placeholder was undefined, and I was happy with that, but there were certain significant issues with doing that. Our current solution has always felt like a hack. A Symbol/standardized name for a placeholder would be perfect.

@paldepind
Copy link
Member Author

Using a symbol sounds like a great idea. The only question is what to call it.

@buzzdecafe
Copy link
Member

something like @@ramda/placeholder i think. and use that as the key in non-Symbol envs

@paldepind
Copy link
Member Author

@buzzdecafe

My opinion:

In particular the name someSpecialPropertyName should not include the word "ramda". This would make it more natural for potential other curry implementations to adopt the same placeholder "specification".

@buzzdecafe
Copy link
Member

fair enough @paldepind , maybe @@__/placeholder

@kevinbeaty
Copy link
Contributor

I think namespacing symbol names is a good idea, so I would vote for @@ramda/placeholder or similar. Other symbols used by ramda could use the same convention @@ramda/whatever. If it's a Symbol that would be useful to expose in the public API, it would be good to export it or use Symbol.for (or just strings for now) as Symbol('whatever') !== Symbol('whatever').

@kevinbeaty
Copy link
Contributor

This would make it more natural for potential other curry implementations to adopt the same placeholder "specification".

Sorry. I don't know how I missed this.... I still vote for namespacing though :)

@paldepind
Copy link
Member Author

I think @@__/placeholder sounds good.

@kevinbeaty
Copy link
Contributor

I'm having a hard time coming up with a reason why it would be beneficial to define the placeholder in a cross-library way... Why would you use a different currying library that works with the ramda placeholder?

That said, if that is the goal, I would prefer something more descriptive, like @@curry/placeholder.

I'm not going to fight for it though. I'll stay quiet now :)

@paldepind
Copy link
Member Author

I'm having a hard time coming up with a reason why it would be beneficial to define the placeholder in a cross-library way... Why would you use a different currying library that works with the ramda placeholder?

There is a myriad of curry libraries in JavaScript. As far as I know only Ramda supports placeholders. But it's a powerful idea so one day some other library might implement the same feature. For the sake of my argument let's assume that ExtremeCurry implements placeholders as well.

I'm writing a program and I really like functional programming so I'm using Ramda and some functional library for dealing with time. All functions in this library are advertised as being curried. The author choose to do so using ExtremeCurry but I as an end user does now know that. So I happily try to pass along my placeholder (R.__) to one of the functions from the time library. This doesn't work. But if both Ramda and ExtremeCurry had used the same placeholder there would be no issue.

All curried libraries could of course expose their own placeholder but not having to do that would be nicer IMO.

Naming it @@curry/placeholder would also be an option. But I don't fancy it too much since another use case might one day be found for the placeholder than currying (Haskell uses the underscore in a bunch of places).

@kevinbeaty
Copy link
Contributor

I get where you are coming from... It could potentially be useful at some point in the future.

My hesitation is mostly claiming a symbol name without namespacing. If you want to use Symbols in a cross library way, you'd have to look it up in the global symbol table using something like Symbol.for. If you are claiming a global name, I think you should either namespace it to your library, or come up with a descriptive, unambiguous, precisely defined, name to claim, i.e., something better than __.

If you punted and just used a magic string (which you would have to do anyway, unless you polyfill) instead of claiming a global Symbol, my hesitation would be greatly diminished.

@paldepind
Copy link
Member Author

I see. That is a valid concern. My best suggestions are @@functional/placeholder or @@placeholder/placeholder. The first is based on the idea that the placeholder is for all functional libraries and the second on the idea that the "placeholder" namespace should be used even though the only thing we'd use in that namespace is "placeholder".

@buzzdecafe
Copy link
Member

perhaps we should follow the practice of browser makers and prefix our names with our own namespace (viz. @@ramda). Then if something gets standardized, we can support that instead.

@paldepind
Copy link
Member Author

If someone takes a decision about the name I will do a PR.

I don't care much but I still think we should invent our own standard. My only reason for doing so would be that I could write something like "all functions in libraryFoo are curried and they accept placeholders as specified in The One An Only Curry Placeholder Standard" in my libraries that uses the Ramda curry function like this.

@buzzdecafe
Copy link
Member

I would prefer something more descriptive, like @@curry/placeholder.

that seems reasonable to me. Either @@curry/placeholder or maybe @@partial/placeholder.

(Frankly, if I had my druthers, I'd pull the placeholder out completely. But I'm pretty sure my druthers would be overruled.)

@CrossEye
Copy link
Member

I would prefer something more descriptive, like @@curry/placeholder.

that seems reasonable to me. Either @@curry/placeholder or maybe @@partial/placeholder.

👍

(Frankly, if I had my druthers, I'd pull the placeholder out completely. But I'm pretty sure my druthers would be overruled.)

I don't have a major objection to the way it is now. I want the abilities we have there, and the only alternative I see to the placeholder is the, admittedly more powerful, but also more complex, idea that lodash has of rearg. I certainly don't love the placeholder, and I have some fondness for the old version that worked with undefined, but that's another discussion, and I do understand that it had serious flaws. But perhaps we should consider something like rearg, or possibly cycle which seems simpler than rearg and which together with flip would give all its capabilities.

@paldepind
Copy link
Member Author

I want the abilities we have there, and the only alternative I see to the placeholder is the, admittedly more powerful, but also more complex, idea that lodash has of rearg

Complex? In usage? Implementing rearg is not hard. I have one right here. Lodash has placeholders as well. Speaking of which: @jdalton, would you be interested in following a placeholder specification?

@CrossEye
Copy link
Member

Complex? In usage?

Yes. Implementations are quite easy, as you demonstrate.

But whereas I easily understand what flip(fn) or fn(1, _, 3) do, it's much less clear what rearg([2, 0, 1], fn) is supposed to mean. Of course one can learn. But it will never be as intuitive, with a list of indices as an API.

And I'm afraid there's a similar problem with a possible cycle, although that's one that you could learn pretty quickly. Does cycle(fn)(a, b, c); //=> fn(b, c, a) or //=> fn(c, a, b)?

@svozza
Copy link
Contributor

svozza commented May 24, 2015

I like the placeholder and use it extensively, I think it's really easy to see what's the programmer's intent is with it. I find it more readable than flip; rearg or cycle don't have any appeal at all either.

@CrossEye
Copy link
Member

I like the placeholder and use it extensively, I think it's really easy to see what's the programmer's intent is with it.

I'm mixed. I don't particularly like the placeholder. I simply don't see anything better. The point I was making, though, is that rearg (or possibly a flip/cycle combination, although that's more difficult) is clearly more powerful than the placeholder, in the sense that everything that can be done with a placeholder can be done with rearg and partial application, but rearg can do things that placeholders cannot do:

    var g = rearg([2, 0, 1], f);

I'm just not sure that buys us anything over the lambda:

    var g = function(a, b, c) {return f(b, c, a);};

Certainly it's shorter, but to me it's much less readable.

@paldepind
Copy link
Member Author

I personally don't find code written with neither rearg nor flip readable. I'd rather just give up on point free at that point. Cycle reminds me of a function in Forth (in Forth everything is point free) called rot for rotate. In the Forth community it's a very common opinion that rot leads to unreadable write-only code.

@CrossEye
Copy link
Member

I find flip often quite readable, especially for binary functions:

var propOf = flip(prop);
// ...
var configProps = propOf(configObj);
// ...
var serviceUrl = configProps('serviceUrl');
// or
var divideBy = flip(divide);
var over10 = divideBy(10);
// ...
log(over10(420));

My Forth is so long ago and not well-remembered. But rot or cycle does seem pretty horrible. The only reason for bringing it up is that it, together with flip, offers the full power of rearg without depending on index numbers for its API. But I don't particularly like either. I would rather just live with what we have and insist that users create a lambda for such argument juggling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants