Example of real world usage of function bind syntax
I need to be using this library! I, myself, used the syntax for virtual
methods quite a bit, and personally replicated a third of your library
without realizing it. Also, it's a lot easier and nicer to type
foo.map(::this.bar)
than foo.map(this.bar.bind(this))
or foo.map(x => this.bar(x))
. I also (ab)used it to create my own DSL for conditionals
that would be equivalent to a condition-less switch
in CoffeeScript and
the like, but that was more of an experiment than anything else.
I'm not entirely sure if it's appropriate, but I just published a library called Trine[1] that takes advantage and displays the power of the proposed function bind syntax. Consider this my upvote for the proposal. :)
It's definitely appropriate, as long as it's clear to users that the ::
syntax is experimental, non-standard and subject to change. This is
actually the kind of usage feedback we were hoping to get : )
Has there been any discussion of anyone championing this proposal for
ES2016? I would very much like to see it land, especially given that I've already been using it extensively in production via Babel. :P
Not sure you should use it in production, just yet...
I'm the champion for this proposal, and I plan on pursuing it. I'm not sure that it will fit into the ES2016 timeline though, given the time remaining and other priorities (like async/await). To be honest, I'm not overly worried about which "train" it leaves on.
Since you brought it up...
I've been considering reducing the scope of the proposal, focusing on the "immediate apply" aspect of the operator, while leaving open the option of adding the other features at a later time. Specifically,
- Remove the prefix form of the operator.
- Restrict the syntax such that an argument list is required after the right operand.
In other words, these forms would no longer be valid under the proposal (although they could be re-introduced in another proposal):
let bf1 = ::obj.foo.bar;
let bf2 = obj::foo;
But this would still be OK:
obj::foo(bar);
Given your experience with the operator and your use cases, would you still be in favor of such a minimal proposal?
I am extremely excited about this extension proposal as well but:
On Thu, Jun 11, 2015 at 10:31 AM, Kevin Smith <zenparsing at gmail.com> wrote:
I've been considering reducing the scope of the proposal, focusing on the "immediate apply" aspect of the operator, while leaving open the option of adding the other features at a later time. Specifically,
- Remove the prefix form of the operator.
- Restrict the syntax such that an argument list is required after the right operand.
I would be significantly less excited about it if this happens. The ability to pass around "lightly" bound references to methods is a big deal imo and a large part of the value in this proposal.
Like you said it doesn't matter what train it gets on, if the new release cadence for ES works as we all hope then it should ultimately stop mattering what release a feature is officially spec'd in. Ideally once a feature reaches candidate stage it should be considered ready, right?
not arguing or anything, and just as parenthesis, but this:
On Thu, Jun 11, 2015 at 4:31 PM, Kevin Smith <zenparsing at gmail.com> wrote:
Not sure you should use it in production, just yet...
is what I keep seeing as pattern:
- here: please try this but don't use in production since not standard (see transpilers early adoption, proto, etc)
- ... developers write code regardless ...
- too late to drop "that" since it's already in production out there
I wish Babel or any other transpilers warned in red everything that does not come from standards, reminding that things could and probably will change, so that it's clear developers can test "at home" but not deploy (unless they truly know what they are doing or they are OK risking changes).
Please don't get me wrong, I do like the :: proposal but the way it's going out, I'm not sure is the best we can do as TC39/developers collaboration.
Best
In Babel all the experimental features are behind flags and the docs (babeljs.io/docs/usage/experimental) are very explicit about their status:
Subject to change
These proposals are subject to change so use with extreme caution. Babel may update without warning in order to track spec changes.
Even the blog posts announcing new features (babeljs.io/blog/2015/05/14/function-bind) have warnings at the top:
Warning: This syntax is highly experimental and you should not use it for anything serious (yet). If you do use this syntax, please provide feedback on GitHub.
So I’m curious to hear what suggestions you have to make this clearer.
From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Matthew Robb
I would be significantly less excited about it if this happens. The ability to pass around "lightly" bound references to methods is a big deal imo and a large part of the value in this proposal.
Definitely agree. Being able to do foo.map(::this.bar)
is really great, and even const extracted = ::foo.bar
is nothing to sneeze at.
I know there's a thread on the issue tracker where a few vocal voices are complaining that they want partial application syntax and bikeshedding on various operator forms related to that, but I don't think that should discourage the excellent benefits that you're giving to everyone but those few.
@Sebastian It would be interesting to explore having a defined method of deprecation for features. Basically say this bind syntax is being dropped, I'd like Babel to be able to transpile all of my SOURCE files to remove it's usage. Has anything like this been discussed before?
- Matthew Robb
I know Babel is kinda explicit but the way it's being promoted and described in every talk I see does not warn, does not talk about caveats, it's like developers don't want to see and believe there are no problems at all.
As example, if I write this code in the live transpiler, which is what developers show at conferences:
var o = {method: function () {
return this;
}};
console.log(::o.method);
I don't see warnings.
Moreover, if I use syntax that might break because of core.js gotchas, I'd like to see warnings in there too. Babel might be the better place, doing a better analysis than a polyfill as core.js is, at recognizing potentially problematic patterns and warn about them too.
For instance, every time I tell developers they cannot trust Symbols with Object.create(null) objects they go like: "uh???!" because they either didn't read Babel or core.js caveats section.
Again, not saying it's your fault, I just think something more could be done (and not even that sure what exactly)
Best
Not really. It would require something like Recast (benjamn/recast) to do nondescructive pretty printing to retain as much of the source formatting as possible. This shouldn’t be necessary though as if you’re using these experimental features extensively enough in production apps to justify the existence of a transpiler for dead experimental features then you’ve already committed a sin.
On Thu, Jun 11, 2015 at 11:37 AM, Sebastian McKenzie <sebmck at gmail.com>
wrote:
then you’ve already committed a sin.
I guess my point is that I think it's a GOOD thing that developers are taking the risk to try these features out in production and rather than focusing on a way to better discourage such usage it may be better in the long run to find a way to make it less risky.
The reality is that even though a feature is not in spec and even if it's eventually dropped for consideration the transpiler is still going to work because it's target is ES5. I think if a team decides they want to use an experimental feature that it SHOULD NOT be discouraged but made clear that support may eventually land on them and/or the maintainer of the plugin for said feature or a plugin that can be used to assist in removal of the feature if the need arises.
- Matthew Robb
On Thu, Jun 11, 2015 at 11:43 AM, Matthew Robb <matthewwrobb at gmail.com>
wrote:
or the maintainer of the plugin for said feature
To expand on this: I would also suggest that all experimental features be implemented as plugins even if doing so will cause a slow down of builds for users of the feature. I would go so far as to say the only features that should be implemented outside of plugins should be stage 1 and higher.
- Matthew Robb
I find the call form of the operator (a::b()
) very useful on its own.
However, I think the main question is, will shipping the prefixed bind or
prefixed call forms of the operator (::a.b
, ::a.b()
), and/or the bind
form of the operator (a::b
), definitely preclude future extension
with partial application, etc, or can those still be worked in somehow? If
there's a way to include all four forms and leave open the future
possibility of extension, I think, as Domenic points out, that we would see
a lot of value from the bind and prefix forms as well.
Here's a cool trick I found using this bind syntax today: Babel REPL babeljs.io/repl/#?experimental=true&evaluate=true&loose=false&spec=false&playground=false&code=class Foo { bar %3D %3A%3Athis.bar%3B bar(){ } }
But it lead me to think that class methods could have ::
prefixed onto
them to suggest that they be lightly bound method references:
class X { ::Y() { } }
- Matthew Robb
I don’t think we should make it easier to shoot yourself in the foot by auto-binding methods (and thus creating new copies of the method for every instance of the class).
I followed the link, but the example was a bit different from this one and you are using the assignment operator.
Equally as hackolicious (using the pending class properties proposal):
class X { Y = () => { ... } }
Le 11 juin 2015 à 17:56, Jordan Harband <ljharb at gmail.com> a écrit :
I find the call form of the operator (
a::b()
) very useful on its own.However, I think the main question is, will shipping the prefixed bind or prefixed call forms of the operator (
::a.b
,::a.b()
), and/or the bind form of the operator (a::b
), definitely preclude future extension with partial application, etc, or can those still be worked in somehow? If there's a way to include all four forms and leave open the future possibility of extension, I think, as Domenic points out, that we would see a lot of value from the bind and prefix forms as well.
The prefixed bind operator won’t preclude the possibility to add syntax for partial application; the only limitation is that those two separable operations could maybe not be conflated into one operator, like the .bind()
method.
A more interesting issue in my view is whether a prefix (::obj.meth
) is the best syntax. An infix alternative like obj->meth
might be interesting if the left term is itself a complex expression.
A more interesting issue in my view is whether a prefix (
::obj.meth
) is the best syntax. An infix alternative likeobj->meth
might be interesting if the left term is itself a complex expression.
You could always use parenthesis:
Right - I think there are going to be two points of contention regarding the prefix form:
- Is a unary prefix operator appropriate, given the somewhat "magic" dereference+bind behavior?
- Does the fact that
::obj.meth !== ::obj.meth
amount to a footgun?
The infix form faces some different challenges:
- Is it okay to encourage the "floating" method style (a la Jussi's library)?
- Aren't we sacrificing polymorphism and is that a bad thing?
Derp - email fail! : )
You could always use parenthesis:
::(whatever).foo
But in any case:
Right - I think there are going to be two points of contention regarding the prefix form:
- Is a unary prefix operator appropriate, given the somewhat "magic" dereference+bind behavior?
- Does the fact that
::obj.meth !== ::obj.meth
amount to a footgun?The infix form faces some different challenges:
- Is it okay to encourage the "floating" method style (a la Jussi's library)?
- Aren't we sacrificing polymorphism and is that a bad thing?
The question isn't really about whether infix and prefix are both good things, the question is about whether it's best to fight both of those battles at the same time.
Anyway, still thinking about it and I really appreciate everyone's input!
On Thu, Jun 11, 2015 at 5:31 PM, Kevin Smith <zenparsing at gmail.com> wrote:
I'm not entirely sure if it's appropriate, but I just published a library
called Trine[1] that takes advantage and displays the power of the proposed function bind syntax. Consider this my upvote for the proposal. :)
It's definitely appropriate, as long as it's clear to users that the
::
syntax is experimental, non-standard and subject to change. This is actually the kind of usage feedback we were hoping to get : )
Great!
Has there been any discussion of anyone championing this proposal for
ES2016? I would very much like to see it land, especially given that I've already been using it extensively in production via Babel. :P
Not sure you should use it in production, just yet...
It's okay, it's used in a very small product and the worst that can happen is that it will bind (heh) us to a specific version of babel until the syntax is removed from the codebase.
I'm the champion for this proposal, and I plan on pursuing it. I'm not sure that it will fit into the ES2016 timeline though, given the time remaining and other priorities (like async/await). To be honest, I'm not overly worried about which "train" it leaves on.
Since you brought it up...
I've been considering reducing the scope of the proposal, focusing on the "immediate apply" aspect of the operator, while leaving open the option of adding the other features at a later time. Specifically,
- Remove the prefix form of the operator.
- Restrict the syntax such that an argument list is required after the right operand.
In other words, these forms would no longer be valid under the proposal (although they could be re-introduced in another proposal):
let bf1 = ::obj.foo.bar; let bf2 = obj::foo;
But this would still be OK:
obj::foo(bar);
Given your experience with the operator and your use cases, would you still be in favor of such a minimal proposal?
I see the most benefits in the immediate invocation forms of the proposal, and have used that more extensively than the other forms. However, working with React, I've found that the prefix form is also very nice thing to have for all the ::this.onClick, etc. things. So yes, I would still be in favor, but to me the prefix form is still a pretty cool nice to have.
Looking at the discussion about partial application thought of as a blocker
for the syntax, I'd prefer to keep the two separate - for example the
proposal I made for partial application [1] is compatible with or without
the bind syntax: foo::bar(qoo, ???)
would be the same as doing
bar.bind(foo, qoo)
.
However, if there's even the remote possibility of getting the non-immediate forms of the bind syntax do a light binding (i.e. always returning the referentially same function), that would be IMO worth blocking the non-immediate forms over to see if it could lead anywhere. Currently as far as I understand, for example React considers all my event listeners changed on every render and removes the listeners and adds them again, because they're bound at render-time.
As a slight offtrack, the slim arrow function would be very handy with this
style of programming. ;) Also, one thing I noticed while writing this
library is that being able to name this
might be interesting. Currently
even in syntax extensions such as flow, there's no natural way to type
annotate this
in standalone functions. However, say we had a syntax like
this:
function (&a, b) { return a + b; }
Where the this
argument would be accessible in the a parameter, it could
be simply type annotated as
function (&a : number, b : number) { return a + b; }
Regarding polymorphism, I don't think the bind syntax changes things much,
it just (possibly) makes polymorphism happen more at the this
slot. Also,
I wonder if in the case of the bind operator the engines could actually
hold an inline cache in the shape of the object instead of the function
itself. I'm also uncertain if the engines currently consider things such as
function getFromUnknownType (key, value, has, get) { if ( has(key) ) { return get(key); } return null; }
polymorphic or if they're able to statically verify that actually the types
in there remain the same regardless of the type of the key
and value
passed in (unless the passed functions are inlined of course).
I wouldn't be too worried about polymorphism though, since the advantages of the syntax allow us to "add" methods to iterables, which means we can do
products ::quickSort(function (b) { return this.price - b.price; }) ::head(k);
versus
products .sort(function (a, b) { return a.price - b.price; }) .slice(0, k);
which is a difference of O(kn) versus O(n²) in worst case time complexity.
- Jussi
Forgot to mention that Trine actually implements the placeholder syntax I proposed: parseInt::partial(_) // returns a unary version of parseInt
(off-topic post)
I'm not entirely sure if it's appropriate, but I just published a library called Trine that takes advantage and displays the power of the proposed function bind syntax. Consider this my upvote for the proposal. :)
Has there been any discussion of anyone championing this proposal for ES2016? I would very much like to see it land, especially given that I've already been using it extensively in production via Babel. :P