{"action":"created","issue":{"url":"https://api.github.com/repos/crystal-lang/crystal/issues/2592","repository_url":"https://api.github.com/repos/crystal-lang/crystal","labels_url":"https://api.github.com/repos/crystal-lang/crystal/issues/2592/labels{/name}","comments_url":"https://api.github.com/repos/crystal-lang/crystal/issues/2592/comments","events_url":"https://api.github.com/repos/crystal-lang/crystal/issues/2592/events","html_url":"https://github.com/crystal-lang/crystal/pull/2592","id":154798610,"number":2592,"title":"Add external/internal names of method/macro arguments","user":{"login":"asterite","id":209371,"avatar_url":"https://avatars.githubusercontent.com/u/209371?v=3","gravatar_id":"","url":"https://api.github.com/users/asterite","html_url":"https://github.com/asterite","followers_url":"https://api.github.com/users/asterite/followers","following_url":"https://api.github.com/users/asterite/following{/other_user}","gists_url":"https://api.github.com/users/asterite/gists{/gist_id}","starred_url":"https://api.github.com/users/asterite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asterite/subscriptions","organizations_url":"https://api.github.com/users/asterite/orgs","repos_url":"https://api.github.com/users/asterite/repos","events_url":"https://api.github.com/users/asterite/events{/privacy}","received_events_url":"https://api.github.com/users/asterite/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":15,"created_at":"2016-05-13T20:56:35Z","updated_at":"2016-05-15T22:00:15Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/crystal-lang/crystal/pulls/2592","html_url":"https://github.com/crystal-lang/crystal/pull/2592","diff_url":"https://github.com/crystal-lang/crystal/pull/2592.diff","patch_url":"https://github.com/crystal-lang/crystal/pull/2592.patch"},"body":"This implements #2583 \r\n\r\n## Pros\r\n\r\n1) You can use an underscore as an external name to disallow passing an argument as positional, **something that can't be done right now**\r\n\r\n```crystal\r\ndef foo(_ x, _y)\r\n  x + y\r\nend\r\n\r\nfoo 1, 2 # OK\r\nfoo 1, y: 2 # Error\r\nfoo x: 1, y: 2 # Error\r\n````\r\n\r\n2) You can pass an argument as a named argument with an identifier that's a keyword (like `class`, or `if`), **something that can't be done right now**\r\n\r\n```crystal\r\ndef say_hi(if condition)\r\n  puts \"Hi!\" if condition\r\nend\r\n\r\nsay_hi if: 1 == 2\r\n```\r\n\r\nWe could actually use this in the compiler's source code, because an `If` node has `then` and `else` properties, but you can't do `If.new(cond, else: some_code)`, you have to either do `If.new(cond,nil, some_code)` or `If.new(cond, a_else: some_ncode)` (both ways are ugly).\r\n\r\nI can also imagine `if: ...` and `unless: ...` are going to be very common, and having a way to refer to an argument like that, instead of going through the double splat, will definitely be useful.\r\n\r\n3) Argument names can be readable for the caller side and the method's body:\r\n\r\n```crystal\r\nclass Time\r\n  # Returns a new Time instance that matches the given *unix_epoch* seconds.\r\n  def self.new(*, unix_epoch seconds : Int)\r\n    do_something_with(seconds)\r\n  end\r\nend\r\n\r\nTime.new unix_epoch: 1234\r\n```\r\n\r\n## Cons\r\n\r\nIt's one more thing to learn. But I think the explanation is pretty simple: if an argument has \"two names\", you use the name on the left for named arguments, on the caller side, and the one on the right inside the method's body.\r\n\r\nMy main fear is the underscore being used all over the place, to prevent matching arguments by name... I'll write in the docs that, if we always do this, it will probably lead to less readable code, so this should only be done for public APIs where you think you might want to change an argument's name, or you absolutely don't want it to be passed by name.\r\n\r\nI personally disliked this feature the moment @waj told me about it. Whenever I told him about double splats and named args he asked \"and external names...?\". Eventually it grew on me, as @ozra [says](https://github.com/crystal-lang/crystal/issues/2583#issuecomment-219028020). Right there we have 3 things that can't be done right now, and these only contribute to better code (more readable code), so of course my vote for this is 👍  now."},"comment":{"url":"https://api.github.com/repos/crystal-lang/crystal/issues/comments/219313308","html_url":"https://github.com/crystal-lang/crystal/pull/2592#issuecomment-219313308","issue_url":"https://api.github.com/repos/crystal-lang/crystal/issues/2592","id":219313308,"user":{"login":"Perelandric","id":1114646,"avatar_url":"https://avatars.githubusercontent.com/u/1114646?v=3","gravatar_id":"","url":"https://api.github.com/users/Perelandric","html_url":"https://github.com/Perelandric","followers_url":"https://api.github.com/users/Perelandric/followers","following_url":"https://api.github.com/users/Perelandric/following{/other_user}","gists_url":"https://api.github.com/users/Perelandric/gists{/gist_id}","starred_url":"https://api.github.com/users/Perelandric/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Perelandric/subscriptions","organizations_url":"https://api.github.com/users/Perelandric/orgs","repos_url":"https://api.github.com/users/Perelandric/repos","events_url":"https://api.github.com/users/Perelandric/events{/privacy}","received_events_url":"https://api.github.com/users/Perelandric/received_events","type":"User","site_admin":false},"created_at":"2016-05-15T22:00:15Z","updated_at":"2016-05-15T22:00:15Z","body":"@asterite \r\n\r\nIsn't the point of your `_` name suggestion to force positional arguments? I wasn't talking about named arguments.\r\n\r\n``` crystal\r\n# Forced position -------v\r\naccounts.transferFrom \"checking\", to: \"savings\", the_amount: 123.45\r\naccounts.transferFrom \"checking\", the_amount: 123.45, to: \"savings\"\r\n```\r\n\r\nIn your OP, under **Pros**, you do have this statement:\r\n\r\n>1) You can use an underscore as an external name to **disallow** passing an argument as positional, something that can't be done right now\r\n\r\nwhich seems to contradict your example. I assumed it was a typo and that you meant \"require\" or \"force\". If it's not, then I guess I just don't understand what you mean."}}