Shorthand for "function" keyword
On Fri, Nov 10, 2017 at 1:11 PM, Laurentiu Taschina <source.spider at gmail.com> wrote:
I would like to propose the standard allow for shorthand form for the normal "function" keyword.
...
Advantages & Motivation
only regular functions can have names, but being so clunky people are discouraged from using the syntax in favor of short but anonymous arrow functions; this is detrimental for stack traces and debugging
That's a myth. The following function has the name foo
, every bit as much
as function foo() { ... }
would:
const foo = () => {
throw new Error();
};
console.log(foo.name); // "foo"
foo(); // Stack trace will show the name "foo"
Try it here on any vaguely-recent version of Chrome or Firefox.
This was first defined in ES2015, the same spec that defined arrow
functions; search the spec for SetFunctionName to see all the various
places it applies. The short version is: If it's assigned to just about
anything but a property on a preexisting object and the name of the thing
it's being assigned to can reasonably be used as a name, that becomes the
name of the function. So obj.foo = () => { }
doesn't assign a name (due
to information leak concerns), but assigning to a constant, variable,
property within an object initializer, parameter as default value, etc.,
all do on compliant implementations.
I think the only thing missing is hoisted declarations for non-function
functions (arrow functions have no declaration form), which doesn't
immediately seem like sufficient cause for a new keyword (but that's not my
call).
-- T.J. Crowder
Doesn't work if it's not pre-declared. [1] (see jsfiddle, a bit too long to post here)
So useless for callback usage, which a very big chunk of the use cases.
While you can argue to kind-of get enough information (it says it's "each" in chrome) with the other way, and having a name there is just preference, the function version does produce better results by providing more information by letting you pick the name yourself. Such as to say "it's an each that does thing X", not just "it's an each." It doesn't make sense to penalize people with a harder to work syntax when trying to do it like that, since it makes the code intent easier to read by both humans and browsers.
As a general nicety it's also a bit easier to work with when refactoring.
Consider converting this...
each(item, () => { // ... });
each(items, function createSum() { // ... });
...into this...
function createSum() { // ... }
each(items, createSum);
Everyone can refactor it easily when the name is already there. You just have to move it up, or outside, or wherever.
--
On Fri, Nov 10, 2017 at 2:58 PM, Laurentiu Taschina <source.spider at gmail.com> wrote:
So useless for callback usage, which a very big chunk of the use cases.
That's very different from the statement "only regular functions can have names" but I can see how you got there. :-)
So naming inline callbacks is your primary motivation for this?
You're not going to have any luck getting func
, fn
, or def
as a
keyword for this. Too much existing code would break. I'm certain I've used
all of those even just in my own code. Just about any short keyword is
going to have the same problem.
Which basically means we're back to the recurring theme of an
explicitly-named form for arrow functions (since you almost never want
function
functions for inline callbacks anyway). That'll probably require
syntax rather than a keyword.
For instance, almost any flag character just inside the opening (
could
work; an arrow seems apt:
const sum = [1, 2, 3].reduce((>add> a, b) => a + b);
...or backticks or quotes or... Technically one only needs an ending delimiter, but it simplifies parsing (human and machine) if there's an up-front indicator of a name; delimiters both sides (two characters) seems reasonable. (If we really wanted to go wild, the same thing could be a declaration form. Ducks head and runs for cover.)
It's a totally solvable problem, but I wonder if the will is there.
-- T.J. Crowder
An idiom that assigns names to arrow functions arguments:
let add, mul; //used to name arrow functions
const sum = [1, 2, 3].reduce(add = (a, b) => a + b);
const product = [1, 2, 3].reduce(mul = (a, b) => a * b);
On Fri, Nov 10, 2017 at 4:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
An idiom that assigns names to arrow functions arguments:
Heh, I actually wrote that at one point in my reply to Laurentiu, then removed it because it was still clunky. :-) It's less clunky when you combine the declarations like that...
-- T.J. Crowder
You're not going to have any luck getting
func
,fn
, ordef
as a
keyword for this. Too much existing code would break. I'm certain I've used all of those even just in my own code. Just about any short keyword is going to have the same problem.
I think understand what you're thinking. When you say existing code will break I assume you're referring to "blah blah is a keyword" errors correct?
If so then I present to you the following question: do compilers actually care for it to be a RESTRICTED keyword?
Is "keyword" always being synonymous with "cant be used as a variable/property/whatever" name a restriction that actually makes sense for all keywords in the language or is it a artifact of simpler times and javascripts origins. Well, of early programming compilers in general really (or assumptions when creating a language), that we never got rid of--even if there is a good reason, I'm pretty sure everyone learned it as a the-thing-you-do before they learned the why you do it.
But getting back to the issue. With how much tit and tat I hear every time someone talks about the browser javascript engines in a conference (compiles, re-compiles, optimization pipelines, this-and-that) you'd think that compilers are sophisticated enough these days that the only reason the compilers "care" is because they literally have an if statement somewhere which checks if it should slap you in the face with "keywords cant be used as variable names", and no other reason.
Solution #1: Implement it as a unrestricted keyword
The following example just to prove a point, but if you use func as a function and variable in the same context in real life maybe the compiler should actually slap you ;) since for backwards compatibility we only really need to support it acting as a variable and for it as a feature we only need it for acting as a synonym for function (both simultaneously is a non-feature).
func func(func) { // keyword name argument
func world() { // keyword name
return " world";
}
let fn = func func() { // variable keyword
name func = func + world(); // argument argument let fn = { // local-variable func: fn func() => func (func) { // property keyword
name keyword argument return func; // local-argument } } console.log(fn.func()); // variable property/function-call } fn(); }
func("hello") // function-call
I've included fn as a keyword for named inline functions just to make the example even more func-y.
Also, as already explained, please note that cases "func: fn func() => func (func) {" will never happen in a backwards compatibility case since code like that is invalid outside of the aliases existing. They're there to answer questions "but if people do X after they are in the language" but honestly the compiling throwing a fit over it is perfectly reasonable there.
Naive rules: it's a synonym for "function" when it's <keyword> <name> ( <anything> ) => OR <keyword> ( <anything> ) {
I think you can apply the rule to just about any keyword you want. In fact you can probably have it as any-random-word (as in all) and it would still work, if you really wanted to (which nobody does of course). I'll spare everyone the silly example.
Solution #2: Implement it as a optional keyword
The idea is that you keep the restricted bit of being a keyword but when it's encountered it disables the other posibility. This works on the basis that you never need it as both a variable (backwards compatibility) or as a function (feature) at the same time.
Rule: func is a keyword so long as there isn't a variable declared before it is used in that scope. If it's used as a function keyword then it can no longer be used as a variable and vice versa. It can always be used as an object property.
So if you have this code:
func something() { let func = 1; // type error: func is a keyword because it was used as a function on line X }
function something(func) { func hello() { // type error: func can not be used as a keyword because it exists as a variable inside this scope } }
function somethingElse() { func func() { // type error: func was used as a keyword, can not be used as a function name in this scope } }
function a() { function b() { let func = 1; } func c() { // this is ok, restriction only applies inside b } }
func x() {
function y(func) { // type error: func was already used as a function keyword }
}
On Fri, Nov 10, 2017 at 7:25 PM, Laurentiu Taschina <source.spider at gmail.com wrote
...
So the TL;DR of all that is: You think func
(or whatever) can be an
identifier or keyword based on context.
Well, quite true, it's certainly been done before (async
for instance).
All I'll say is: Good luck with that, for this purpose. :-)
I'll flag up an ASI gotcha you'll need to avoid: When defining func
function definitions, you'll need "[no LineTerminator here]" after the
closing )
of the parameter list. Why? Because otherwise this is a
function call followed by a standalone block thanks to ASI:
let a = func()
{ console.log("blah"); };
(Which is why AsyncFunctionDefinition has "[no LineTerminator here]" after
async
.) But again: It's been done before, so...
I'm having trouble believing saving a couple of characters will have the weight to make it happen, and personally wouldn't favor it, but frankly that doesn't mean anything.
-- T.J. Crowder
I'm having trouble believing saving a couple of characters will have the
weight to make it happen
I think this is the crux of the issue. What is the cost vs. benefit of the proposal, and do these benefits outweigh the tradeoffs?
The only benefit seems to be saving a few characters. The costs being an increase in complexity in the ways you can define a function, an increase in the complexity of parsing for functions, and the need to endure a candidate stage process for the removal of a few characters. To me, the costs far outweigh the benefit of just using the existing ways of declaring functions.
TLDR; to me this is -1.
Eli Perelman
The only benefit seems to be saving a few characters. The costs being an
increase in complexity in the ways you can define a function, an increase in the complexity of parsing for functions, and the need to endure a candidate stage process for the removal of a few characters. To me, the costs far outweigh the benefit of just using the existing ways of declaring functions.
I believe you're being unfair here. It's obviously not just 4 characters when it's such a common construct. If "let" was 3 words instead of 3 letters, like say "assign variable value" what do you think would happen? Would people think of it as "just 18 more characters". No, people would ignore it completely no matter it's usefulness or correctness and world continue to use "var" exclusively.
The cost of not changing it is that people will slowly and surely gravitate to avoiding using "function" ever (if they can), and thus the construct that can be named falls out of favor. And history has shown it happens, particularly in javascript, given the popularity of $ and _ for a good number of libraries, and at the same time the unpopularity of perfectly usable but tooLongToTypeNamed native versions. Of course in large part it's ease of use and consistency and all that, but the first thing I ever hear they being sold on is how short/easy they are. Looking at other cases today you have the situation of things like Kotlin vs Java, or swift vs objective C. From Kotlin's site it literally gets described as "Concise, simple and very easy to read (and write)" in their first example. But even looking at just javascript's past, people clearly care enough to make their own divergent language just to get it have things be shorter—and if they can't do that, then the next best thing is to misuse the language.
This wouldn't maybe be a particular big issue, but javascript is more-or-less "build complex functions with a bunch of small functions." Callbacks imply lots of functions, promises imply a lot of functions, functions are usually split into lots of smaller single-use-functions, functions often times return functions, etc.
Anonymous functions are already kind of annoyance born of laziness but with arrow functions (and how they can not easily have a name in the context of a callback) they're becoming particularly routed into the language. I think at this point people looking at a stack trace seeing the callbacks named will look at it as something strange. Don't you think that it's strange that that's normal?
With regard to the difficulties of this going though, I could have presented the some-greatest-feature-ever and you could have just copy/pasted that 2nd part of your sentence there to shoot it down. All I'll say is that it's just a synonym to an existing thing (even let is more complex), for users it's just an nicer version that helps with moving away from anonymous functions (you wouldnt really ever need to use "function" ever again, much like you dont need "var" now). And as far as implementing goes, if we were to create javascript form scratch would we actually use "function" as the keyword for creating functions and all the other overlyLongMethodNames? I don't think so, every other programming language doesn't seem to think so, with how concise most libraries are written the community doesn't seem to think so, and even the language javascript presumably copied those habits from probably no longer thinks so anymore. I think even with out other benefits just getting closer to the language that everyone thinks it should have been like is valuable enough to warrant the hassle. I assume that's the very reason we have let now too.
Obviously we should use 𝓕 or something like that. I'm sure it's been proposed, although I can't find it at the moment. Yes, I know all the reasons why that's a bad idea.
But come on. Are you worried about typing more characters? Use an IDE with auto-complete, or snippets. Are you worried about payload size on the wire? Zipping makes it virtually irrelevant.
Bob
Who uses functions instead of arrow functions and methods these days, anyway? (I know, people who haven't adopted ES2015+)
Hahah...
On Sun, Nov 12, 2017 at 6:09 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
Who uses functions instead of arrow functions and methods these days, anyway? (I know, people who haven't adopted ES2015+)
Laurentiu's been fairly clear about his/her use case: Named inline
callbacks. I don't think he/she cares whether the functions are arrow
functions, so long as they have names. (Laurentiu, correct me if I'm wrong
there.) Right now, if you want to pass a named inline callback to
something, the simplest way is to use the function
keyword.
(I don't agree with the suggestion, but he/she's been clear about it.)
-- T.J. Crowder
On Sun, Nov 12, 2017 at 1:11 AM, T.J. Crowder < tj.crowder at farsightsoftware.com> wrote:
On Sun, Nov 12, 2017 at 6:09 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
Who uses functions instead of arrow functions and methods these days, anyway? (I know, people who haven't adopted ES2015+)
Arrow functions fail to convey 'this' There is this library I'm using that calls the callback with .call( instance, ... ) and an arrow function does not get the instance handle. I've tried to convince the library maintainer to pass it as an argument rather than trying to encode it as this, so that both old and new function styles can be used; but not with much success.
Arrow functions passed as callback to Node.js addons execute 20% slower than regular 'function()' functions.
Can't say that javascript itself is any slower; but when interfacing to native code they are slower..
On Sun, Nov 12, 2017 at 9:56 AM, J Decker <d3ck0r at gmail.com> wrote:
Arrow functions passed as callback to Node.js addons execute 20% slower than regular 'function()' functions.
Wow. Is that written up somewhere one could read more?
When you say "Node.js addon," are you excluding built-in Node APIs?
-- T.J. Crowder
On Sun, Nov 12, 2017 at 2:14 AM, T.J. Crowder < tj.crowder at farsightsoftware.com> wrote:
On Sun, Nov 12, 2017 at 9:56 AM, J Decker <d3ck0r at gmail.com> wrote:
Arrow functions passed as callback to Node.js addons execute 20% slower than regular 'function()' functions.
Wow. Is that written up somewhere one could read more?
When you say "Node.js addon," are you excluding built-in Node APIs?
Basically it was an empty function I was calling with some simple parameters It was only something I found through experimentation. It really has nothing to do with NodeJS other than it was an addon, it was just interacting with the V8 engine.
Writing file... Wrote in 199 .. reading... ()=>{} Read in ... 300000 3394
function(){} Read in ... 300000 2690
d3x0r/sack.vfs/blob/master/tests/largeStreamtest.js#L15
and line 18 is the other one.
In the function I just increment a counter.... 126% or 79.25% ...but then
there is quite a bit of overhead in my library to parse the JSON. that's
quite a nested object
{\"a${i}\":{\"b${i}\":{\"c${i}\":{\"d${i}\":123}}}}
Hmm... changed the file that was being generated to just a simple string, and the timings were closer (but was less than 1 second run) so I added some generation counts and ran the tests in various orders... and I guess maybe it was because of other factors that coincidentally showed as ()=>{}
being slower than function(){}
function(){} Read in ... 100000 958 ()=>{} Read in ... 100000 906 ()=>{} Read in ... 100000 784
function(){} Read in ... 100000 783 ()=>{} Read in ... 100000 924
function(){} Read in ... 100000 779 function(){} Read in ... 100000 881 ()=>{} Read in ... 100000 805
then just reversed the two tests in the original script and the timing stayed the same in-order... but then would show function() as being the slower one.
My Bad; thanx for asking about that though I would have been stuck with that misconception for a long time.
That sounds like a major V8 bug that should be reported - there should be zero performance difference between an arrow function and a normal ES5 function. They're completely equivalent apart from ES5 functions being constructible and arrow functions forming a closure around the current call context. In particular, I'd expect them to be faster, not slower.
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
Who uses functions instead of arrow functions and methods these days,
anyway? (I know, people who haven't adopted ES2015+)
In my previous message on the paragraph where I talked about how "let" would suffer the same if it was longer I had some words about how we'll eventually reach the stage where people actively avoid "function" by abusing classes, objects and arrow functions everywhere, because they think it's hip and cool even though it's mostly fluff with little concrete benefit. It felt like too much of a tangent so scraped it. But...
Didn't think someone would post no mere 2 messages later on how we should need to write a pointless class or anonymous function to get it. Seems I should have just left that part in. :)
With regard to ES2015+ I actually do use the bleeding edge more or less of the spec and also typescript on the server. I've used most of them more then I can count. I have my opinions on when and where some of features are useful. Some are useful all the time, some only useful when used sparingly, and detract more then they add. Blindly just throw "new shiny features" of the language and you won't end up with better code, you'll just end up with enterprise javascript fizzbuzz. [1]
Are you worried about typing more characters? Use an IDE with
auto-complete, or snippets.
The problem can not be easily fix'ed with snippets. I wasn't even pointing at using "func" by the way, I've mentioned several from other languages as examples. So long as it's not over 4 characters (and it's not some convoluted symbol character notation, or awkward letter positioning) I don't particularly care which one is chosen.
*But anyway, lets say "fn" was used to denote named "arrow functions" (technically we don't need the arrow in this case so I'll omit it in the example). *
Take something like the following,
let sorter; if (someCondition) { sorter = fn sortByA() { /* 100 lines of code / } } else { sorter = fn sortByA() { / 100 lines of code */ } }
Closest equivalent code would be like this:
let sorter; if (someCondition) { sorter = (function sortByA() { /* 100 lines of code / }).bind(this) } else { sorter = (function sortByA() { / 100 lines of code */ }).bind(this) }
Though more realistically you would write it like this:
const self = this; // avoid the need for binding this let sorter; if (someCondition) { sorter = function sortByA() { /* 100 lines of code / } } else { sorter = function sortByA() { / 100 lines of code */ } }
Hard to say which is more annoying, binding "this" everywhere or managing a "self" as an alias to "this".
And if we really have to use our lord and savior arrow functions, the closest equivalent is this:
let sorter; if (someCondition) { const sortByA = () => { /* 100 lines of code */ }
sorter = sortByA;
} else { const sortByA = () => { /* 100 lines of code */ }
sorter = sortByB;
}
Prettier but silly.
You see the same variable duplication problem with the more common case of callbacks, be it to async functions or some sort of looping function:
each(migrations, fn mergeRequirements( /* ... */ ) { // much stuff });
Earlier one a very clever workaround was suggested,
let mergeRequirements; each(migrations, mergeRequirements = ( /* ... */ ) => { // much stuff });
It's very clever, but of course in practice you'd never do it. It's just awkward.
In the real world what I do for example is either prioritize never having "this" as some requirement of any code if I can help it. Don't place your helper functions as methods and suddenly no need for this.methodCall. When practical, you can avoid the usual public methods by having functions and passing the instance in rather then calling the method on the instance; this is the right approach if your code with classes would just produce a giant inheretence mess rather then anything useful. But of course IF classes, or rather some sort of instantiatable type with methods attached (since you can chose not use the "class" keyword and do it the old fashion way if you want mixins), are actually the correct approach for representing the thing in question, the solution with a "self" alias for "this" is the easiest fix.
But really I shouldn't even have to do that at all, it should just work.
I won't go back on what I've already said, but my case isn't with just arrow functions but normal "function" too. It's too long! hard to write, hard to read. I would have to make ligatures for it get shorter in an IDE, since when do we need IDEs anyway? (I use one but IDEs being that intrusive is like depending on a intermediate language) Because it's long as I described already, people just gravitate away from it; look at Naveen's message. And when they have to use it, from my experience, the anonymous version wins, because it's shorter. Anonymous functions aren't exactly necessary, the most legitimate case I can think of is when it's so short its the name is longer then the function itself, ie. (a, b) => a + b,
and these are rare, very rare in real life then compared to in blog articles and whatnot (in my experience at least, but may be just a coding style thing).
Actually with that in mind, if the shortnend versions REQUIRED a name every time, that would actually be perfectly fine by me. I realize this would be maybe a bit extreme, but there wouldn't be this "[no LineTerminator here]" issue Cowder brought up earlier, since "func()" could only mean call to a function "func" as there's no name there ;)
Arrow functions already fill in the niche of shortest possible syntax to express an anonymous function so it wouldn't be any "loss".
--
Arrow functions are preferable primarily because they don't allow a new "this" which is otherwise the cause of a lot of confusion/bugs. The other reason is terseness. ()=>{} / input=>output etc. So what value are you
adding for introducing a shorthand for an already defunct construct
(function)? It's like offering a shorthand for var
, which has no use
anymore.
If I've misunderstood and you're instead offering a shorthand for const x = ()=>{}
in the form fn x(){}
. I think that just hurts readability and
clarity, as well as consistency with the anonymous form ()=>{}
.
Otherwise I'm not getting your point
the this in fat-arrows is irrelevant to frontend-developers who prefer to use static functions to manipulate json-data, rather than class instantiated-objects which are difficult to serialize / reconstruct when baton-passing between frontend <-> backend.
On Mon, Nov 13, 2017 at 10:22 PM, Laurentiu Taschina < source.spider at gmail.com> wrote:
Are you worried about typing more characters? Use an IDE with auto-complete, or snippets.
The problem can not be easily fix'ed with snippets.
You forgot to say why not, veering off into not pointing specifically at
"func", which is a non-sequitur. Configure your IDE so that when you type
fn
and press a space, it expands to a function template. Done.
-- T.J. Crowder
Also, most decent text editors, including Atom, Sublime, Vim, and Emacs, support such snippets. (Vim/Emacs call them macros, but they're functionally the same concept mostly.)
For all new syntax proposals or discussions, I recommend writing a Babylon plugin + Babel transform to prove that it's even possible before presenting it for feedback.
Hello,
I would like to propose the standard allow for shorthand form for the normal "function" keyword.
No other special meaning, just shorter (preferably at most 4 characters).
Advantages & Motivation
I believe there are also various subjective "bonus" points, much how arrow functions and class methods have gotten a lot of love for just being shorter and people having a personal need for a thing they write a ton being shorter, but I won't go into those.
I believe the most common, outside of not having a keyword at all, are in particular order: "fn" (rust), "func" (go) and "def" (ruby, python). Out of these I believe "func" is the best candidate since it maintains the highest readability.
It should be said that allowing both "func" and "fn" for example is also an option but the benefits, such as using it to express a difference between some functions, eg. single use / general / inline, may cause more chaos and confusion then they're worth. I also personally believe the language should have a easy way to defined a named arrow function and the keyword "fn" may be very useful in this sense, ie. fn doStuff(sum, item) => { ... }
Disadvantages
None that I can think of.
Previous Discussion
Searched the mailing list but could only find "exotic" syntax proposals. If this has already been discussed please let me know.