Example of real world usage of function bind syntax

# Jussi Kalliokoski (9 years ago)

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

# Isiah Meadows (9 years ago)

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.

# Kevin Smith (9 years ago)

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?

# Matthew Robb (9 years ago)

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?

# Andrea Giammarchi (9 years ago)

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:

  1. here: please try this but don't use in production since not standard (see transpilers early adoption, proto, etc)
  2. ... developers write code regardless ...
  3. 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

# Sebastian McKenzie (9 years ago)

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.

# Domenic Denicola (9 years ago)

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.

# Matthew Robb (9 years ago)

@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
# Andrea Giammarchi (9 years ago)

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

# Sebastian McKenzie (9 years ago)

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.

# Matthew Robb (9 years ago)

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
# Matthew Robb (9 years ago)

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
# Jordan Harband (9 years ago)

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.

# Matthew Robb (9 years ago)

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
# Domenic Denicola (9 years ago)

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).

# Bucaran (9 years ago)

I followed the link, but the example was a bit different from this one and you are using the assignment operator.

# Jeff Morrison (9 years ago)

Equally as hackolicious (using the pending class properties proposal):

class X { Y = () => { ... } }

# Claude Pache (9 years ago)

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.

# Kevin Smith (9 years ago)

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.

You could always use parenthesis:

Right - I think there are going to be two points of contention regarding the prefix form:

  1. Is a unary prefix operator appropriate, given the somewhat "magic" dereference+bind behavior?
  2. Does the fact that ::obj.meth !== ::obj.meth amount to a footgun?

The infix form faces some different challenges:

  1. Is it okay to encourage the "floating" method style (a la Jussi's library)?
  2. Aren't we sacrificing polymorphism and is that a bad thing?
# Kevin Smith (9 years ago)

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:

  1. Is a unary prefix operator appropriate, given the somewhat "magic" dereference+bind behavior?
  2. Does the fact that ::obj.meth !== ::obj.meth amount to a footgun?

The infix form faces some different challenges:

  1. Is it okay to encourage the "floating" method style (a la Jussi's library)?
  2. 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!

# Jussi Kalliokoski (9 years ago)

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

[1] esdiscuss.org/topic/syntax

# Jussi Kalliokoski (9 years ago)

Forgot to mention that Trine actually implements the placeholder syntax I proposed: parseInt::partial(_) // returns a unary version of parseInt

# Isiah Meadows (9 years ago)

(off-topic post)