Shorthand For Inline Functions

Hi,

Inline functions are just like regular functions but without a body. They take some arguments and return an expression.

function add(a, b) {
  return a + b;
}

My idea is to provide a cleaner way to write them like this...

function add(a, b) a + b;

Actually, the real reason I'd like to have them is to make object getters less verbose.

So instead of this...

const person = {
  get name() {
    return "Ethan";
  }
}

We can do...

const person = {
  get name() "Ethan";
}

Let me know what you think. Thanks!

2 Likes

That seems like it would be encouraging getters as a pattern, which i personally think is the opposite of what we should do.

1 Like

That's such a weird argument. Whether you write a getter or a function is a design decision that has nothing to do with syntax. Are you saying that people don't write getters because they're too much typing, and if they suddenly became a few keystrokes shorter, people would start spamming them (and that would be bad)?

Perhaps another way to view it is: if the key problem this new syntax solves is with getters, then the value this new syntax brings, compared to the cost of adding something new to the language, is based on how often people are using getters in the first place.
If the use of getters is uncommon and increasing their usage is not a goal then the value add of the syntax turns out to be low.

1 Like

Put that way, I can see the point.

But it doesn't need to be only for getters, every method could use this.

2 Likes

This vs:

const add = (a, b) => a + b;

I actually make frequent use of accessors for some things, and I still don't see a worthwhile use for this. The amount of effort required for no functional gain is just too much. Sure, you save a few keystrokes, but only at the cost of making an already difficult parsing job a bit more difficult. Stack on top of this that the savings only apply to getters since we already have arrow functions.

Incorrect.

o = {
   longform() { return this.bar },
   shortform() this.bar,
   arrowform: () => this.bar, // something completely different
}

Forgot about that notation. Almost never use it. Don't see the advantage of that over the "longform" except to save a few keystrokes at the cost of readability.

I know this has been rejected in the past, but I'm curious if anyone would find thin arrow functions appealing for this.

o = {
   longform() { return this.bar },
   shortform() this.bar,
   arrowform: () => this.bar, // something completely different
   thinarrow: () -> this.bar, // same as longform/shortform
   thinarrow2() -> this.bar, // same as longform/shortform
}

That would make the only difference between "->" and "=>" how "this" gets bound? And normally you want this to be bound, so "->" doesn't seem all that useful. It doesn't help with the getter problem either, as there's no way to assign to the getter.

Maybe that's all we really need here:

{
  get x: () => this.#x
}

I see no reason why we shouldn't allow syntax like that. It doesn't really feel like we're spending any syntax budget to do that, rather, we're just opening up a natural and almost-expected avenue for how shorthand and long-form syntax of functions currently work.

{
  getX() {
    return this.#x
  }
  getX: () => this.#x

  get x() {
    return this.#x
  }
  get x: () => this.#x // Why not??
}

That takes us back to being "something completely different". For classes this would be an instance field rather than an accessor defined on the prototype (though that's consistent with the second getX) and for object literals this would be wrong.

Not to say I would personally be opposed to it. I'm just not sure its what the original shortform syntax was after.

@senocular - you're right, it does diverge quite a bit from the original idea.

So, I just threw it together as a new top-level proposal here.

One reason I like @ethanlal04's original idea is that it makes the grammar more consistent. Arrow functions allow two forms of function body:

ArrowFunction :
  ArrowParameters => ConciseBody
ConciseBody :
  [lookahead ≠ `{`] ExpressionBody
  { FunctionBody }

Regular functions and methods are more restrictive, they only allow { FunctionBody }:

// simplified for readability, not actual grammar
FunctionExpression :
  function optName(Params) { FunctionBody }
MethodDefinition :
  Name(Params) { FunctionBody }
  get Name() { FunctionBody }
  set Name(Params) { FunctionBody }

Why not allow ConciseBody for all functions?

From what I understand, the primary reason for introducing arrow functions into the language was to provide a syntactic shorthand for normal functions. They decided later to also add auto-this-binding capabilities to arrow functions, so that they weren't just a shorthand. Now, we're attempting to make a shorthand for normal functions again - which means arrow functions must not be doing their job at being a good shorthand for normal functions.

That said, I do see the appeal for the proposed shorthand.

Only because I just happened to be looking at this in reference to -> rejection, it looks like lexical binding was on the board for arrow functions as early as at least 2011.

https://web.archive.org/web/20110509070104/http://wiki.ecmascript.org/doku.php?id=strawman:arrow_function_syntax#:~:text=(fat%20arrow)%20for%20lexical

There's that and a whole lot more that didn't make the cut.

Edit: including some of the cut features (tl;dr) because they're pretty interesting:

// [thin arrow]
let identity = (x) -> x;

// Nullary arrow function starts with arrow (cannot begin statement)
let nullary = -> (preamble + ': ' + body);

// Use # to freeze and join to nearest relevant closure
function return_pure() {
  return #(a) -> (a * a);
}

// An explicit ''this'' parameter can have an initializer
// Semantics are as in the "parameter default values" Harmony proposal
const self = {c: 0};
const self_bound = (this = self, a, b) -> {
  this.c = a * b;
};

// Object intialiser shorthand: "method" = function-valued property
// with dynamic ''this''
const obj = {
  method() -> {
    return => this;
  }
};

// Name binding forms [...]
var warmer(a) -> {...};
1 Like

Sorry for the late response.

Hi @theScottyJam,

I dunno much, but this seems to work differently in arrow methods of regular objects.

const person = {
  walk1: () => {
    console.log(this);
  },

  walk2() {
    console.log(this);
  },
};

person.walk1(); // {}
person.walk2(); // { ...person }

It's not as good as new syntax, but here's a getter / setter shorthand I've been playing around with once in a while. (I rarely use get / set to be honest.)

const A = ( x, y ) => 
    Object.create( {}, {
        x: { get: () => x, set: v => x = v },
        y: { get: () => y, set: v => y = v },
        sum: { get: () => x + y }
    } );

const a = A( 5, 5 );
a.sum; //10
a.x = 10;
a.sum; //15

Of course, get / set are unnecessary for x and y above. Here's a cleaner form:

const A = ( x, y, self = { x, y } ) =>
	Object.defineProperties( self, {
        sum: { get: () => self.x + self.y }
    } );

const a = A( 5, 5 );
a.sum; //10
a.x = 10;
a.sum; //15

What do you think? Other than the hideous "Object.defineProperties", it's actually shorter. You write the property name only once.

(You need self, because as you noticed, arrow functions don't treat this the way normal functions do.)

To get rid of the ugly "Object.X" part, you'll have to use a helper constructor like:

const Self = ( a, b ) => 
    b ? 
    Object.defineProperties( a, b ) :
    Object.create( {}, a );

And now we can have appropriately meaningful and beautiful syntax:

const A = ( x, y ) => 
    Self( {
        x: { get: () => x, set: v => x = v },
        y: { get: () => y, set: v => y = v },
        sum: { get: () => x + y }
    } );

//Or:
const A = ( x, y, self = { x, y } ) =>
	Self( self, { 
        sum: { get: () => self.x + self.y } 
    } );
1 Like

Fun historical fact, Firefox used to support exactly this syntax! Warning: expression closures are deprecated - JavaScript | MDN

4 Likes