coffeescript icon indicating copy to clipboard operation
coffeescript copied to clipboard

Proposal: Optional (pre-last) function arguments

Open celalo opened this issue 5 years ago • 4 comments

It has been submitted before #1091, #4148 but it has been a while since last discussed so I'd like bring it forward again. I've been an avid coffeescript user since the early days and I missing this feature all the time.

instead of:

fn = (optional_arg1, optional_arg2, cb) ->
  unless cb?
    unless optional_arg2?
      cb = optional_arg1
    else
      cb = optional_arg2

wouldn't it make it sense to have it as:

fn = (optional_arg1?, optional_arg2?, cb) ->

or better yet, decorating optional arguments with default values:

fn = (optional_arg1 ?= true, optional_arg2 ?= false, cb) ->

for example:

retriveRecords = (perPage ?= 20, page ?= 1, cb) ->

above function can be used neatly as:

retriveRecords page, cb for page in [1...20]

or as:

retriveRecords -> #callback result with arguments page=20 & page=1

my workaround at the moment is like this:

retriveRecords = ({perPage, page}, cb) ->
  perPage ?= 20
  page ?= 1

retriveRecords perPage: 50, ->
retriveRecords page: 5, ->
retriveRecords perPage: 50, page: 5, ->
retriveRecords {}, ->

celalo avatar May 10 '20 00:05 celalo

Can you please include what the expected output JavaScript would be for your examples?

Also are there any ECMAScript proposals at any stage of the TC39 approval pipeline that overlap with this?

GeoffreyBooth avatar May 10 '20 15:05 GeoffreyBooth

Can you please include what the expected output JavaScript would be for your examples?

fn = (opt?, cb) -> do cb

could compile into

fn = function(opt, cb) {
  if (typeof cb === "undefined") {
    return [opt, cb] = [undefined, opt];
  }
  return cb();
};

fn = (opt ?= 42, cb) -> do cb

could compile into

fn = function(opt, cb) {
  if (typeof cb === "undefined") {
    return [opt, cb] = [42, opt];
  }
  return cb();
};

retriveRecords = (perPage ?= 20, page ?= 1, cb) ->

could compile into

retriveRecords = function(perPage, page, cb) {
  if (typeof cb === "undefined" && typeof page === "undefined") {
    return [perPage, page, cb] = [20, 1, perPage];
  } else (typeof cb === "undefined") {
    return [perPage, page, cb] = [perPage, 1, page];
  }
};

Also are there any ECMAScript proposals at any stage of the TC39 approval pipeline that overlap with this?

I could not find anything related.

celalo avatar May 10 '20 17:05 celalo

So I think you would need to compile into == null rather than === undefined to match the output of ? in other contexts (i.e. null or undefined, not just undefined). Though there's the complication that since CoffeeScript 2 default parameters are only applied for undefined, to match ES6, so arguably ? in the function parameter context should follow this undefined-only rule.

This also looks challenging to implement. Keep in mind it would need to apply also for => functions and class/object methods. Are you up for taking this on?

One more consideration is simply, how useful is this? Node uses the style in its callbacks of putting err first specifically to save people the hassle of having to parse parameters like this. The object style, e.g. fn(options), has also become increasingly popular especially with destructuring (fn = ({ foo, bar }) ->). It seems less common nowadays to see functions with lots of arguments, especially optional ones in the non-final position.

GeoffreyBooth avatar May 11 '20 02:05 GeoffreyBooth

Here's a tidy general-purpose implementation: #try

retriveRecords = (perPage, page, cb) ->
  defaults = [20, 1]
  [perPage, page, cb] = [
    defaults[0 ... retriveRecords.length - arguments.length]...
    arguments...
  ]

The signature length would probably need to be hardcoded at compile-time, and for fat arrow functions the parameter list would need an arguments replacement like soaking args.... defaults could also be inlined to not clutter the scope.

CoffeeScript (and JS for that matter) typically has callback arguments as the last function parameter to allow inline function definitions at the call site. It's true that this creates the hassle of having to rearrange the arguments if some of them are optional.

I too found this to be a common pattern/burden in my projects and am all for getting this language feature.

I'm uncertain of the proposed ?= syntax however. I'd expect ?= to give default arguments back their CoffeeScript v1 meaning.

Inve1951 avatar Aug 16 '20 10:08 Inve1951