-
Notifications
You must be signed in to change notification settings - Fork 30
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
Drop the this
binding altogether
#44
Comments
Or even return to the earlier proposal for the foreign properties (not a bind at all for the obj::prop) var prop1 = Symbol();
var prop2 = { get: function() { return this::prop1 + 1 } };
var x = {};
x::prop1 = 123; // assign (same mechanics as WeakMap)
console.log(x::prop2); // 124 - retrieve (like if prop2 is defined as property on x)
// and x is still empty postulate that just thoughts.... |
The main issue as I see it is the chaining. Its the easy sell because you can clearly define a problem that needs solving. Given something along the lines of: function* where($this, predicate) {
for (var item of $this) {
if (predicate(item))
yield item;
}
}
function* select($this, selector) {
for (var item of $this) {
yield selector(item);
}
}
function first($this) {
for (var item of $this) {
return item;
}
throw Error("no items");
} if you try to chain these at present you get: let ugly = first(select(where([1, 2, 3, 4, 5], x => x > 2), x => x.toString())); which reads in complete reverse of what actually transpires. I think its quite clear that the full power of generators and Iterables are being held back because of this nesting. A simple solution would be a mechanism that simply passes the left-operand, as the first parameter, to the right-operand function. let better = [1, 2, 3, 4, 5]::where(x => x > 2)::select(x => x.toString())::first(); Also, he other criticism was that :: looked odd. Is single colon a better (available?) operand? cc:@zenparsing |
@MeirionHughes I'm only proposing dropping the rest, and just keeping the chaining. I'm intentionally steering away from talk regarding the |
For what little it’s worth, as someone who’s enjoyed using |
Yet another argument in favor of dropping the unary form is it will be less controversial for the general public to swallow the binary form and it poses less spec dilemmas so it has better chances of landing in a spec quicker. The unary form could still land as a separate addition in a future ECMAScript version but coupling them in one proposal delays both of them. |
So is this suggestion about dropping the whole proposal in favor of giving |
@InvictusMB, it means only that we allow only the following syntax: PrimaryExpression "::" PrimaryExpression "(" ArgumentList ")" And treat foo::bar(a,b,c) as bar.call(foo, a, b, c) Standalone foo::bar is prohibited (not yet specified) Thus, no bindibg ever involved, and no new closure formed respectively. |
Yes, I'm proposing to drop all binding in general, both instance and
argument binding, from the proposal. Those parts are still very
contentious, so IMHO they should be considered separately. People are still
uncertain whether it should even be an operator, since it looks like it
should be persistent when it doesn't.
But with pipelining, it's something people are way more interested in. The
only real disagreement I've seen is the operator of choice (to a small
extent) and whether it should involve `this`.
…On Sat, Feb 4, 2017, 10:05 InvictusMB ***@***.***> wrote:
So is this suggestion about dropping the whole proposal in favor of giving
:: a meaning of functional pipelining?
Is foo::bar going to be equivalent to _.partial(bar, foo) ?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#44 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AERrBEG-qkSC2y3qt0TVZLaWe2nb2l9iks5rZJO4gaJpZM4Lt7f3>
.
|
And yes, it is kind of pipelining, but it uses a hidden this parameter. I feel, that TC39 has very strange implicit resistance against any improvements related to any this usage cases. |
I wouldn't quite go that far. They are just aware of the fact `this` isn't
super intuitive to many. They aren't completely anti-`this`, especially
considering their apparent view of the private state proposal. They just
appear highly pragmatic.
On Sat, Feb 4, 2017, 11:07 Anatoly Ressin <notifications@github.com> wrote:
And yes, it is kind of pipelining, but it uses a hidden *this* parameter.
I feel, that TC39 has very strange implicit resistance against any
improvements related to any *this* usage cases.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#44 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AERrBIdRjKXFJuaNYi6JD1RbdJL9ORUKks5rZKIwgaJpZM4Lt7f3>
.
|
@Artazor
That example implies that result of LHS expression is partially applied to function in RHS and the resulting function is invoked with arguments in parentheses. Essentially LHS expression is being pipelined as a first argument to RHS instead of being passed as @isiahmeadows Pipelining as a parameter will help people coding in functional style, while pipelining as In functional style you want to start a chain with a But in OOP style you want to start a chain with an Occasionally you also want to pass around methods while preserving their binding to the owning instance. This happens when OOP style code meets functional style code. |
@InvictusMB I'm afraid that in the initial post |
(sorry, just reformated my message - typing on phone is terrible) |
@Artazor Nevertheless, I can envision the need for 3 different operators:
They all have their respective use cases and fit for different programming styles.
const {map, flatten, uniq} = _;
const names = items
|> map(item => item.value)
|> flatten()
|> uniq()
const foo = {
bar() { return this; }
bang() { return this; }
};
function bang() {
return this::foo()::bar();
}
foo
::bar()
::@baz()
::bang() I don't have a preference for either In addition to that, always using |
I don't want to put words in ts39's mouth but if they found :: to (quote) look weird (unquote) then hopefully they'll say the same for |> because I'm not a fan. Primarily because it requires two hands to type |>, while :: or ~> can be done solely by the right hand. |
@MeirionHughes You’ve got me curious: how is it that |
I'm not a fan of const foo = [ a => a + 1 ];
// How to do foo::0(42) or foo::[0](42) ?
const symbol = Symbol();
const bar = {
[symbol](a) { return a + 1; },
symbol() { throw new Error(); },
baz(a) { return a + 2; },
};
// How to do bar::symbol(42) or bar::[symbol](42) ?
// How about bar::.baz(42) ? If foo
::foo.bar()
::baz()
::foo.bang()
// Or maybe
foo
.bar()
::baz()
.bang() And to me that's a great improvement by itself. |
@MeirionHughes @bathos @Volune |
To clarify, my main suggestion here is to break apart the two proposals, so
they can be considered more independently.
- Function pipelining: great for virtual methods, etc., and makes
functional logic more linear.
- Partial application/binding: great for event listeners, concise
functional callbacks, etc., while being able to ignore the arguments a
function takes at the call site.
These are two very independent concepts, and not everyone agrees with both.
So I feel it's best to separate the two proposals formally.
…On Sun, Feb 5, 2017, 03:20 Jeremy Judeaux ***@***.***> wrote:
It is convenient to use :: for method access
I'm not a fan of :: to access methods, as there are other ways to access
a method. How will this operator work in these cases:
const foo = [ a => a + 1 ];// How to do foo::0(42) or foo::[0](42) ?
const symbol = Symbol();const bar = {
[symbol](a) { return a + 1; },
symbol() { throw new Error(); },
baz(a) { return a + 2; },
};// How to do bar::symbol(42) or bar::[symbol](42) ?
// How about bar::.baz(42) ?
If :: does not care about accessing methods, at least we can probably
already do:
foo
::foo.bar()
::baz()
::foo.bang()
// Or maybe
foo
.bar()
::baz()
.bang()
And to me that's a great improvement by itself.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#44 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AERrBFL-0L5m3xRTFtzuyOaZIb-McDABks5rZYZogaJpZM4Lt7f3>
.
|
If I remember correctly that was related to the unary prefix form of |
@InvictusMB I'm not really pulling the If
And I doubt it prevents a future introduction of an operator that does method resolution. |
@Volune |
Is method resolution really being considered? It seems pretty much orthogonal to the point of this proposal to me and I don’t get how it entered the picture. Aside from seeming like a weird feature to graft on here, I believe the only precedent for manipulating scope resolution in JS is the deprecated |
@InvictusMB I suggest use Another reason is instance scope resolution seems not have very big value compare to lexical scope resolution (just my feeling). And I believe (and agree) it's why this issue suggest to drop them at current stage. |
@bathos According to the proposal, I've also updated my original proposal to clarify. |
@isiahmeadows That was my understanding, and I think it’s a wise plan. What I was commenting on is that recently, both in this thread and in other threads on this board, there’s been discussion of using Do we know why this is coming up repeatedly? Is it that the |
I think it's mostly the reliance on |
As I hinted with #45, I think the binding operator would be more useful (and less confusing to C++ devs) if it were changed to mean |
I'm sorry. Have you met functional programming before? Not to mention that even with this not being accepted in forever, there is already libraries out there written to use the pipelining. |
I think what confuses me most about the prefix syntax is that |
If we can clear up some of the issues with the pipeline operator (multiple arguments and integration with method calls) I would completely be in favor of changing |
A similar thing could be said about the That way |
@acutmore Yeah, I always found |
in my opinion
function pipelining should be written as make |
@leemotive So it's just syntax sugar, what's the benefit of using it instead of Anyway, this proposal seems already dead? What's the future plan, @zenparsing ? |
@hax syntax is robust against |
@ljharb Most things in JS can be |
@hax i would love to see that happen, yes. However, the slippery slope argument doesn't invalidate this specific syntactic improvement. |
@ljharb Sorry, it's not my intention. I gave this example because past TC39 decisions make me a impression that TC39 would very unlikely to introduce any pure syntax sugar which do not have other significant benefit. And I remember I'm wondering if the taste of TC39 has changed now? |
@hax one of the interesting properties of syntactic sugar in a dynamic language is that it can open up compiler improvements. So not only can the code be clearer to the reader but also it gives the compiler more information to work with. For example adding I would imagine this bind syntax might also open the door for some optimisations |
@acutmore |
If we plan to repurpose In the original proposal, we use For the consistency, we'd better drop |
Great observation! And I agree completely. I would still like to see if we can provide some kind of syntactic support for method extraction, but it will likely need to be a unary operator. We probably don't want to have a sigil inside of the brackets, though. Maybe just a prefix let a = &obj.x;
let b = &obj['x'];
let c = &obj->x; // ;) |
I could get behind unary |
@zenparsing Prefix a // <- missing ;
&obj.x // useless now... But maybe future: a // <- missing ;
&obj.x |> accept_a_func |
@hax Yes, I thought about the ASI issue, but (as with prefix |
@zenparsing Yes, it just add another new token to Another reason I prefer This is my subjective feeling, but groovy's I even consider postfix operator may be better though there is not much choice of token... |
Since the pipeline operator/proposal handles the chaining use-case, what's wrong with |
@lukescott |
To me, the most important use case of the function pipelining import { distinct, join, sort } from 'linq';
let a = [ 8, 42, 5, 9, 2, 5 ];
a::distinct()
::sort( x => -x )
::join(", ")
|> console.log; The reason why I prefer the Therefore I propose changing the operator for “this pipelining” from |
On tc39/proposal-pipeline-operator#110 I've done a summary of the similarities and differences between the Bind Operator and the Pipeline Proposal and I found out that the part promoted by @isiahmeadows is really the only part we need. The rest can be quite easily done with the Smart Pipelines. Be sure to check the issue out, as this is IMHO the most probable way of getting the “function pipelining”, as Isiah calls it, to the specs! |
IMO, pipeline is different and can't replace bind operator. Check my comment at |
This proposal maybe refocus to cover #5 mainly, as I think that use case can't be covered by the pipeline operator. TL; DR: Use bind operator on destructive functions assignment. function foo({ ::doA, ::doC }) {
doA();
doC();
}
const bar = {
a: 1,
c: 2,
doA() {
console.log(this.a);
},
doC {
console.log(this.c);
}
}
foo(bar);
// Output: (With bind operator)
// 1
// 2
// Output: (Without bind operator)
// undefined
// undefined I think this would fit nicely between functional programming, and the oop spirit of Javascript. This currently could be done with something like this function foo(bar) {
const { doA, doC } = bar;
doA.bind(bar);
doC.bind(bar);
doA();
doC();
} But it's not practical, as I would need function foo({ ::doA: baz }) {
baz
} In #5 was a discussion about where the binding should belong, and I think the binding should belong to the destructive property ( Don't know what you think about this, or if you just want to let this die completely. |
Edit: Here's specifically what I'm proposing:
object::func(foo, bar, baz)
, equivalent tofunc.call(object, foo, bar, baz)
object::func
, equivalent tofunc.bind(object)
::object.func
, equivalent toobject.func.bind(object)
Note that this is independent of the syntax or semantics of the function pipelining itself (see #26 for an in-depth discussion on that and the old binding operator, and #40 for a more recent proposal specific to that), and is exclusively focused on dropping the binding syntax.
The resource usage for general
this
binding leads to a lot of confusion:Those of us familiar with the proposal would immediately know this, but those less familiar would expect the opposite (which would be much more wasteful of memory, because JS doesn't have the lifetime management awareness of Rust and C++).
Also, for the binary version,
object::method
without an immediate call doesn't really help much at all for expressiveness.Here's what I propose: drop the
this
binding altogether, and keep it limited to just immediate calls. I'm not proposing anything about where the object is passed (usingthis
/first argument/etc.), or even the operator used, just not including binding in the proposal.First, here's the equivalents for each one, using
Function.prototype.bind
and arrow functions, just for comparison:Function.prototype.bind
is already easily optimized for a single argument. In addition, property and method binding is rarely chained (I've never seen it done, ever). How often do you see anything like this? Chances are, probably never, or if you have, it was likely an obvious refactor.Additionally, if you need to bind after chaining, it's fairly easy to just create a new variable, and naming isn't usually hard for simple cases like these.
But the method chaining still has its benefits:
Method chaining will avoid ugly nested calls like this:
That would make things way easier to read, because the steps are more sequential, and fewer parentheses are involved. Functional languages frequently have something like this for similar reasons, like Elm/LiveScript/OCaml/F#'s
x |> f
operator. They helpfully avoid parentheses due to their low associativity and left-to-right application, making logic much easier to read.Wrapper libraries can leverage ES modules and Rollup's tree-shaking feature to not ship more than necessary, yet still retain the convenient pseudo-method syntax. You could create a Lodash clone that only calls the methods you need, so if you only use
map
,filter
,reduce
, andforEach
, you only bundle those at runtime, even when you install the whole library from npm. Basically, what you don't use, you don't pay for.So I still want the binary version to allow calls, but let's get rid of the
this
binding. It's confusing and unintuitive for newcomers, and hardly provides any benefit in practice.The text was updated successfully, but these errors were encountered: