Shorthand for "function" keyword

# Laurentiu Taschina (7 years ago)

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

  • the keyword often takes as much if not more space then the arguments, almost always as much as the name if not more
  • because it's so clunky people are encouraged to abuse arrow functions (arrow functions shouldn't have "shorter" as their selling point for when they're not needed IMO)
  • 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
  • all other modern languages use the shorter form
  • since it's just an alternative (much like let / const) only with no difference in meaning there's no particular drawback (especially if it's easy to read an already common alternative name is used)

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.

# T.J. Crowder (7 years ago)

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

# Laurentiu Taschina (7 years ago)

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.

--

[1] jsfiddle.net/89t198uu/1

# T.J. Crowder (7 years ago)

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

# Allen Wirfs-Brock (7 years ago)

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);
# T.J. Crowder (7 years ago)

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

# Laurentiu Taschina (7 years ago)

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.

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 }

}

# T.J. Crowder (7 years ago)

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

# Eli Perelman (7 years ago)

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

# Laurentiu Taschina (7 years ago)

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.

# Bob Myers (7 years ago)

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

# Naveen Chawla (7 years ago)

Who uses functions instead of arrow functions and methods these days, anyway? (I know, people who haven't adopted ES2015+)

# Michael DeByl (7 years ago)

Hahah...

# T.J. Crowder (7 years ago)

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

# J Decker (7 years ago)

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

# T.J. Crowder (7 years ago)

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

# J Decker (7 years ago)

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.

# Isiah Meadows (7 years ago)

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

# Laurentiu Taschina (7 years ago)

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

--

[1] EnterpriseQualityCoding/FizzBuzzEnterpriseEdition

# Naveen Chawla (7 years ago)

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

# kai zhu (7 years ago)

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.

# T.J. Crowder (7 years ago)

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

# Isiah Meadows (7 years ago)

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

# Rick Waldron (7 years ago)

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.