-
Notifications
You must be signed in to change notification settings - Fork 1
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
Naming bikeshed #1
Comments
(See also prior discussion to tc39/proposal-function-helpers#17.) I would be more comfortable with using the word “uncurry” if “curry” was a term used anywhere else in the core language, but it’s not used anywhere else in the language, and so that’s at least two steps for a learner to understand: (1) what function currying is and (2) what “uncurrying” therefore is. Someone cannot understand “uncurrying” without understanding “currying”, which is a concept that is present nowhere else in the core language. Yes, currying is common enough in a lot of (but not all) functional-programming styles, but, as you know, several TC39 members are against introducing currying elsewhere in the core language (due to performance concerns, ecosystem-schism concerns, aesthetic concerns, etc.); see tc39/proposal-pipeline-operator#221. And unless currying is present elsewhere in the core language, I am reluctant to introduce uncurrying to the core language too. 🍛 @ljharb’s “callBind” library is also often used, but the “callBind” name comes from how “unThis” is the simplest name I can think of right now that describes what it does: it gets rid of But I’m open to whatever name. 🎨🚲🏠 |
I very much do not like "uncurryThis", because currying is a confusing advanced concept that we shouldn't be promoting, and understanding "uncurry" requires one understands "curry". I also don't like I certainly prefer "callBind" - the reason I named the package that way is because it's literally |
@ljharb: Your point here is well taken, but I would say that insofar that we can call a function that uses the The original function doesn’t change, and a new function is returned, but we do often use verbs to denote pure non-mutating functions as if they were mutating its input in place, instead of returning a new copy. (We could take a page from proposal-change-array-by-copy and call it But if you think that
|
I do, because of the way it'd be naively polyfilled, as |
I don't like callBind that much. It's based on the "call" function with already provides no descriptive value for what it does, and is easily confused with the apply function. Then we're tacking on "bind" after it, and we're left with an even worse name. I hope the bind-this syntax proposal can help people forget that .call() and .apply() was ever a thing, and we can provide a better name from the start for a function like this, instead of using a name that harkens back to functions that'll become almost useless once bind-this syntax is in. Edit: I'll also add, the first time I saw a callBind definition, I almost had to set down with a pencil and paper to figure out what was going on. (the .call() make the "this" arg the first param, but then the bind is binding a this arg, but wait, it's being called on .call(), not on the instance, so the "this" value we're dealing with in .bind() is a different one then what .call() is dealing with, and... now I'm lost) |
I strongly dislike |
A problem with |
unThis does feel like a name I could get used to, but I agree that it's a little confusing on first site. It sort of reads as "take this function, and make it so it doesn't accept a 'this' anymore", which is only half correct. The word "uncurry" doesn't feel like it belongs here either. I see the relationship, but it sort of stretches its definition. thisAsArg() is so far my favorite. It's a bit of a mouthful, but it's still short, and it's clear. But, I do still like unthis(). |
imo it's not an implementation detail - "binding Function.prototype.call" is the semantics it has. It does require users know what call and bind do, but I don't see why that's a problem - this proposal already requires users to know pretty intimately how function calling and |
Another problem with callBind is that it doesn't even do a great job of explaining the semantics either. Someone who has an intuition of .call() and .bind(), and who sees .callBind() could very well assume that the underlying implementation went something like this: Function.prototype.callBind = function(thisArg, ...args) {
return this.call(thisArg, ...args).bind(thisArg)
} or some other similar implementation. There's lots of ways to combine those two actions. The name "callBind" sounds like you're first using .call(), then you're using .bind(). Similar to how .flatMap() has you first use .flat() then use .map(). In reality, you're not actually calling .call(), you're just binding to it. |
The actual semantics of |
Whoops, you're right. Still though, you're performing both of those actions, while a callBind is not doing both a .call() and a .bind(). |
@bakkot: If |
Sorry, I didn't mean to say it implied in-place mutation. I don't think it does. Rather, the problem is that it implies that |
Right, I see now. Do you feel that |
Not to the same extent, no. |
What about I've always think in this approach as transforming a method "coupled" to a class/prototype in a function that can be used with any instance of that class. Like uncoupling map from Array.prototype to use with any ArrayLike |
I'd throw in |
I used |
@jridgewell - could you explain the devirtualize one? I'm not sure how that term fits, probably because there's a meaning to "virtual" that I'm unaware of. @zloirock - I don't think unbind would work. It implies it'll do the opposite of .bind(), e.g. fn.bind(x).unbind() should bring you back to |
const silo = {
launch: function() {
this.launchTheMissiles();
}
}
// Half serious
const launchFrom = silo.launch.solo()
const launchFrom = silo.launch.lifted()
const launchFrom = silo.launch.extracted()
const launchFrom = silo.launch.aside()
const launchFrom = silo.launch.descoped()
const launchFrom = silo.launch.pigeon()
// Quarter serious
const launchFrom = silo.launch.pulled()
const launchFrom = silo.launch.detached()
const launchFrom = silo.launch.elevate()
const launchFrom = silo.launch.privilege()
const launchFrom = silo.launch.appropriated()
const launchFrom = silo.launch.freed()
const launchFrom = silo.launch.spared()
const launchFrom = silo.launch.loosed()
const launchFrom = silo.launch.extricated()
const launchFrom = silo.launch.unbuckled()
// Not very serious, and also made up
const launchFrom = silo.launch.flomp()
const launchFrom = silo.launch.grunk()
const launchFrom = silo.launch.tream()
const launchFrom = silo.launch.yump()
const launchFrom = silo.launch.wat()
const launchFrom = silo.launch.bliggit()
const launchFrom = silo.launch.skriff()
const launchFrom = silo.launch.rontered()
const launchFrom = silo.launch.loffle() I like "pigeon", because |
Virtualized methods are methods which have to lookup the function value from an object at runtime: const foo = { name: 'foo', method() { console.log('bar', this.name); } };
const baz = { name: 'baz', method() { console.log('qux', this.name); } };
function test(obj) {
return obj.method();
}
test(foo);
test(baz); The actual
Devirtualized methods generally take the context object they're working on as the first argument. But the important part is that they no longer require an object lookup to determine function value at runtime, the function is known statically. This is exactly what this proposal accomplishes, we can take a virtual method and make it devirtualized: const method = foo.method.devirtualize();
method(baz); |
That nomenclature seems even less clear than "uncurry" to me. I'd also not consider virtualization unique to functions; your "devirtualized" example there still has |
Ok. I see the relationship, but I think this is actually an orthogonal principle to what this proposal achieves. Take this scenario: function doSomething(obj) {
obj.fn()
} the Likewise, in these code snippets: function fn() {
console.log(this.x)
}
fn.call({ x: 2 }) // 2
fn.devirtualize()({ x: 2 }) // 2 the function is not really virtual. But, that's simply because it's not found on an arbitrary object. |
The word |
"unX" definitely must be the opposite of "X" in every sense, or it would confusing. |
It's not possible to make something opposite to something else in all sense, it all depends solely on the point of view. |
I agree, which is why a negated name (“un” anything) is a poor choice. |
Can the word "lexical" be appropriated, or is it too much of a stretch of the definition? obj.fn.lexicalThis();
obj.fn.lexicalize();
obj.fn.thisLuthored(); 🤔 |
“Lexical” would refer to where the function body was created; i don't see how it applies here. |
unshiftThis ? 🤔 |
Is this expected to be so common that a short name is preferable to a long one? Because long names could just be like: fn.useFirstArgumentAsThis();
// OR
fn.prependThisAsArgument();
// OR
fn.useThisAsFirstArgument(); There could be some nice symmetry if there's common need for appending as well, i.e. having a |
My guess is that a number of projects won't use it at all, but there will also be some projects that use it a whole ton (especially those which try and protect against prototype pollution). That said, the way this method gets used will be on relatively simple lines of repeated code, so I don't think it would be that bad to have a slightly longer method name, even in projects where it would get used a lot. Another potentially longer name, that I think fits the behavior well. fn.addReceiverParam() And here's how this might look in the wild // With long name
const ArraySlice = Array.prototype.slice.addReceiverParam()
const StringSlice = String.prototype.slice.addReceiverParam()
const ArrayMap = Array.prototype.map.addReceiverParam()
// With short name
const ArraySlice = Array.prototype.slice.unThis()
const StringSlice = String.prototype.slice.unThis()
const ArrayMap = Array.prototype.map.unThis() The longer name doesn't really add that much noise IMO. |
I’ve called the helper I use for this “topicalize” for a long time. No strong opinion, just tossing it out there since I didn’t see it mentioned. |
Both "demethodize" and "topicalize" don't make sense to me. I have no idea what "topic" means, and "methodize" isn't a thing (and "method" doesn't have a universal definition anyways). |
I do like @VitorLuizC's idea of |
To me, “topic” means the topic that the pipe operator “Demethodize” seems understandable insofar that developers might understand “method” to mean “function that uses However, it is definitely true that “method” does not have a universal definition anyways (e.g., mdn/content#11237). That is a more compelling reason to me why “demethodize” would be undesirable. I actually am becoming a little more positive towards All of these choices have at least some undesirable characteristics, and we’re just picking the least bad one. If we reach Stage 1, it’ll probably be worth reviewing what our goals with naming should be, in order of priority. |
FYI, there exist community implementations of
Aside: I am not suggesting |
I suspect, if this proposal were accepted with a name (as opposed to syntax) that people would want to write the inverse and name it in a clear way. If this proposal chooses |
This might lead to the assumption that it would work similar to My favorites so far:
|
Never too early for a naming bikeshed! (This need not be resolved until going for stage 3, of course.)
I've always called this
uncurryThis
, which I think is a more precise description of what it does thanunThis
. That's also what I've seen it called elsewhere: x, x, etc.The text was updated successfully, but these errors were encountered: