{"issue":{"state":"open","pull_request":{"diff_url":null,"html_url":null,"patch_url":null},"user":{"avatar_url":"https://secure.gravatar.com/avatar/34a9fc8f846a8645cf4df511e8ec2c49?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"34a9fc8f846a8645cf4df511e8ec2c49","url":"https://api.github.com/users/TrevorBurnham","login":"TrevorBurnham","id":224895},"milestone":{"state":"open","open_issues":4,"url":"https://api.github.com/repos/jashkenas/coffee-script/milestones/8","due_on":"2011-12-25T08:00:00Z","creator":{"avatar_url":"https://secure.gravatar.com/avatar/24fb0088507077f7852adaec4f6b679f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"24fb0088507077f7852adaec4f6b679f","url":"https://api.github.com/users/michaelficarra","login":"michaelficarra","id":218840},"number":8,"title":"next major (2.0)","description":"","closed_issues":0,"created_at":"2011-04-10T01:15:36Z"},"updated_at":"2011-11-06T04:28:06Z","labels":[{"name":"change","color":"178c3c","url":"https://api.github.com/repos/jashkenas/coffee-script/labels/change"},{"name":"enhancement","color":"84b6eb","url":"https://api.github.com/repos/jashkenas/coffee-script/labels/enhancement"},{"name":"notice","color":"d9ff00","url":"https://api.github.com/repos/jashkenas/coffee-script/labels/notice"}],"assignee":null,"closed_at":null,"comments":14,"url":"https://api.github.com/repos/jashkenas/coffee-script/issues/1091","body":"## Short version\r\n\r\nSplats soak up any number of values from 0 to infinity. As a result, you can only use one splat per argument list or array pattern. But there are cases where you want a single optional value, or more than one (potentially non-sequential). Implementing this every time is tedious and increases refactoring effort, making it a high-value language feature.\r\n\r\nThe proposed syntax is\r\n\r\n    (required, optional?, additional...) ->\r\n\r\nor in array pattern-matching\r\n\r\n    [first, optional?, additional...] = arr\r\n\r\nSingle optional arguments would take priority over splatted arguments, with leftward optional arguments taking priority over rightward ones. Optional arguments could be used in conjunction with default arguments, as in\r\n\r\n    (required, optional ?= {}, additional...) ->\r\n\r\nAlso, I suggest that the current `(arg = default) ->` implementation should be deprecated in CoffeeScript 1.1 in favor of the more versatile `(arg ?= default)` syntax.\r\n\r\n## Uses in function arguments\r\n\r\nLet's say that I have a function of the form\r\n\r\n    report = (options, callback) ->\r\n\r\nHaving the `callback` as the last argument is good Node-y style, but `options` should be, well, `optional`. So I can either write\r\n\r\n    report = (options..., callback) ->\r\n      options = options[0]\r\n\r\nor\r\n\r\n    report = (options, callback) ->\r\n      [options, callback] = [{}, options] if typeof options is 'function'\r\n\r\nNeither is nearly as clean or self-documenting as\r\n\r\n    report = (options ?= {}, callback) ->\r\n\r\nLet's take another example that uses an optional argument in conjunction with a splatted argument. I'm defining a function where I want *at least* one value to be in a splatted argument, whenever possible. I have a `setTimeout` wrapper that takes the form\r\n\r\n    timeout = (millis, func, args...) ->\r\n\r\nand I want `millis` to default to `0`. So all of the following should be valid:\r\n\r\n    timeout 30000, puts, 'Thanks for waiting'\r\n    timeout 500, -> puts 'Goodbye'\r\n    timeout -> puts 'Hello'\r\n\r\nCurrently, implementing this API requires a rather annoying series of swaps:\r\n\r\n    timeout = (millis, func, args...) ->\r\n      if typeof millis is 'function'\r\n        args.shift func\r\n        func = millis\r\n        millis = 0\r\n      setTimeout (-> func.apply null, args), millis\r\n\r\nIt gets even worse as you have more arguments, since you have to \"shift\" each one.\r\n\r\nThe optional function argument syntax would allow the function to be implemented more readably (and with more efficient JavaScript output) with\r\n\r\n    timeout = (millis ?= 0, func, args...) ->\r\n      setTimeout (-> func.apply null, args), millis\r\n\r\n(Note that this still doesn't take care of the case where a func and args are given but no millis, because this has to be addressed by checking the type of the first argument.)\r\n\r\nNote that if you want to ensure that a splatted argument receives at least one value, you should make it a separate argument and concatenate them, e.g.:\r\n\r\n    attack = (firstTarget, otherTargets..., options ?= {}, callback) ->\r\n      targets = [firstTarget, otherTargets...]\r\n\r\n# Uses in pattern matching\r\n\r\nHonestly, I can't think of any strong use cases in array pattern matching, but I believe the feature should be implemented nonetheless for consistency with splats.\r\n\r\nDefault values in array pattern matches were previously proposed at [issue 810](https://github.com/jashkenas/coffee-script/issues/810), but rejected as too complex. Unless anyone has a strong use case for it, I think the `?=` syntax should only be available for function arguments, not for pattern matching.\r\n\r\n## Default values: `?=` vs. `=`\r\n\r\nFor the last argument(s) in a function, the `?=` syntax would be somewhat redundant with the existing `=` syntax for default arguments. There is a subtle difference between them: `?=` uses `arguments.length`, while `=` uses, er, `?=` (confusing, right?), which means that the latter would override a given `null` or `undefined` while the former would not.\r\n\r\nI think the best way to remove this confusion is to make `=` and `?=` equivalent (the familiar `=` syntax should probably be kept for the benefit of those coming from other languages), so that there's only one default argument behavior: one that doesn't override explicit `null`/`undefined`. That is, both\r\n\r\n    (required, options ?= {}, callback) ->\r\n    \r\nand\r\n\r\n    (required, options = {}, callback) ->\r\n\r\nshould compile in such a way that if two arguments are given, the second is used as the callback.\r\n\r\n## Implementation details\r\n\r\nWe need a length check for each optional value. (I'm going for ease of implementation here. An optimized version would nest the conditionals so that fewer length checks would often be performed.) Assume that `arguments.length` is assigned to `__len`. Here's what the existing splat syntax compiles to (breaking each assignment onto its own line):\r\n\r\n    foo = (a, b...) ->\r\n    # a = arguments[0], b = 2 <= __len ? __slice.call(arguments, 1) : [];\r\n    \r\n    foo = (a..., b) ->\r\n    # a = 2 <= __len ? __slice.call(arguments, 0, _i = __len - 1) : (_i = 0, []),\r\n    # b = arguments[_i++];\r\n    \r\n    foo = (a, b..., c) ->\r\n    # a = arguments[0],\r\n    # b = 3 <= __len ? __slice.call(arguments, 1, _i = __len - 1) : (_i = 1, []),\r\n    # c = arguments[_i++];\r\n\r\nNow here's how the implementation would go for optional arguments, with the initial assignment of `_i = (index of first optional argument, ignoring splats)`:\r\n\r\n    foo = (a, b?) ->\r\n    # a = arguments[0],\r\n    # b = 2 <= __len ? arguments[_i++] : void 0;\r\n    \r\n    foo = (a?, b) ->\r\n    # a = 2 <= __len ? arguments[_i++] : void 0,\r\n    # b = arguments[_i++];\r\n    \r\n    foo = (a, b?, c) ->\r\n    # a = arguments[0],\r\n    # b = 3 <= __len ? arguments[_i++] : void 0,\r\n    # c = arguments[_i++];\r\n    \r\n    foo = (a?, b?, c) ->\r\n    # a = 2 <= __len ? arguments[_i++] : void 0,\r\n    # b = 3 <= __len ? arguments[_i++] : void 0,\r\n    # c = arguments[_i++]; \r\n\r\nIf a default value is given, substitute it for `void 0`. Obviously the expressions of the form `x = void 0` can be stripped. (That postfix increment on the last `_i` can also be stripped, but I've kept it here because it's in the current implementation.)\r\n\r\nThe only changes needed to the splat implementation for it to interoperate would be 1) to use `_i` as the `slice` start position if there are preceding optional arguments, and 2) to only change the value of `_i` in such a case if the splat is non-empty. For all other purposes, optional arguments would work the same way as \"required\" arguments, since they take priority over splats. Examples:\r\n\r\n    foo = (a?, b...) ->\r\n    # a = 1 <= __len ? arguments[0] : void 0,\r\n    # b = 2 <= __len ? __slice.call(arguments, (_i = __len - 1), _i + 1) : [];\r\n    \r\n    foo = (a..., b?) ->\r\n    # a = 2 <= __len ? __slice.call(arguments, (_i = __len - 1) - 1, _i) : [],\r\n    # b = 1 <= __len ? arguments[_i] : void 0;\r\n    \r\n    foo = (a?, b..., c) ->\r\n    # a = 2 <= __len ? arguments[_i++] : void 0,\r\n    # b = 3 <= __len ? __slice.call(arguments, (_i = __len - 1) - 1, _i) : [],\r\n    # c = arguments[_i++];\r\n    \r\n    foo = (a, b..., c?) ->\r\n    # a = arguments[0],\r\n    # b = 3 <= __len ? __slice.call(arguments, (_i = __len - 1) - 1, _i) : [],\r\n    # c = 2 <= __len ? arguments[_i++] : void 0;\r\n\r\nThe size of the compiled output should give you some idea of how much effort this feature will save for CoffeeScripters who use it.\r\n\r\nWhat do y'all think?","html_url":"https://github.com/jashkenas/coffee-script/issues/1091","number":1091,"title":"Optional function arguments (i.e. single-arg splats)","id":555234,"created_at":"2011-01-27T03:30:06Z"},"action":"created","comment":{"user":{"avatar_url":"https://secure.gravatar.com/avatar/e9e852b18079792bc5f4ec52b8e37c53?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","gravatar_id":"e9e852b18079792bc5f4ec52b8e37c53","url":"https://api.github.com/users/mcmire","login":"mcmire","id":7371},"updated_at":"2011-11-06T04:28:06Z","url":"https://api.github.com/repos/jashkenas/coffee-script/issues/comments/2643932","body":"Perhaps only the `fn = (x, y?, z) ->` feature could be implemented for now? That's still pretty useful. And if you really want `fn = (x, y ?= 5, z) ->` you could say `fn = (x, y?, z) -> y ?= 5`.","id":2643932,"created_at":"2011-11-06T04:28:06Z"}}