{"action":"created","issue":{"url":"https://api.github.com/repos/eclipse/golo-lang/issues/326","labels_url":"https://api.github.com/repos/eclipse/golo-lang/issues/326/labels{/name}","comments_url":"https://api.github.com/repos/eclipse/golo-lang/issues/326/comments","events_url":"https://api.github.com/repos/eclipse/golo-lang/issues/326/events","html_url":"https://github.com/eclipse/golo-lang/issues/326","id":121843841,"number":326,"title":"Ambiguous functions declaration, overloading and shadowing","user":{"login":"yloiseau","id":7151021,"avatar_url":"https://avatars.githubusercontent.com/u/7151021?v=3","gravatar_id":"","url":"https://api.github.com/users/yloiseau","html_url":"https://github.com/yloiseau","followers_url":"https://api.github.com/users/yloiseau/followers","following_url":"https://api.github.com/users/yloiseau/following{/other_user}","gists_url":"https://api.github.com/users/yloiseau/gists{/gist_id}","starred_url":"https://api.github.com/users/yloiseau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yloiseau/subscriptions","organizations_url":"https://api.github.com/users/yloiseau/orgs","repos_url":"https://api.github.com/users/yloiseau/repos","events_url":"https://api.github.com/users/yloiseau/events{/privacy}","received_events_url":"https://api.github.com/users/yloiseau/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/eclipse/golo-lang/labels/bug","name":"bug","color":"fc2929"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/eclipse/golo-lang/milestones/16","html_url":"https://github.com/eclipse/golo-lang/milestones/3.1.0","labels_url":"https://api.github.com/repos/eclipse/golo-lang/milestones/16/labels","id":1397347,"number":16,"title":"3.1.0","description":"Golo 3.1.0","creator":{"login":"jponge","id":25961,"avatar_url":"https://avatars.githubusercontent.com/u/25961?v=3","gravatar_id":"","url":"https://api.github.com/users/jponge","html_url":"https://github.com/jponge","followers_url":"https://api.github.com/users/jponge/followers","following_url":"https://api.github.com/users/jponge/following{/other_user}","gists_url":"https://api.github.com/users/jponge/gists{/gist_id}","starred_url":"https://api.github.com/users/jponge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jponge/subscriptions","organizations_url":"https://api.github.com/users/jponge/orgs","repos_url":"https://api.github.com/users/jponge/repos","events_url":"https://api.github.com/users/jponge/events{/privacy}","received_events_url":"https://api.github.com/users/jponge/received_events","type":"User","site_admin":false},"open_issues":6,"closed_issues":4,"state":"open","created_at":"2015-11-06T09:34:01Z","updated_at":"2015-12-14T09:49:37Z","due_on":null,"closed_at":null},"comments":13,"created_at":"2015-12-12T09:17:49Z","updated_at":"2015-12-14T14:18:15Z","closed_at":null,"body":"Synthesis of comments on #312 #313 \r\nLinked to #320\r\nProbably linked to #275 \r\n\r\nWe have an issue regarding the resolution of overloaded functions, more\r\nspecifically when using variable arity ones (varargs)\r\n\r\n## A little history\r\n\r\nWhen using the literal notation for function references\r\n(`^module::function`) or the `Predefined::fun` function without\r\nspecifying the function arity (`fun(\"function\", module.class)` or\r\n`fun(\"function\", module.class, -1)`), for which the literal notation is\r\njust syntactic sugar, if the referenced function was overloaded, that is\r\nthe function has multiple definitions with different arities, the return\r\nfunction reference used to be undefined. The first found was return, but\r\nwith no guaranty on with this first one would be. \r\n\r\nFor instance, with code like \r\n\r\n    function foo = |x| -> ...\r\n\r\n    function foo = |x, y| -> ...\r\n\r\n    let f = fun(\"foo\", currentModule.class)\r\n\r\n`f` could be either of the two functions.\r\n\r\nA correction was applied to raise\r\na `AmbiguousFunctionReferenceException` in this case, to avoid guessing.\r\nThis force the developer to be explicit on the required arity by using\r\n`fun(\"foo\", currentModule.class, 1)` or `fun(\"foo\", currentModule.class,\r\n2)`, instead of silently using one or the other.\r\n\r\nThe problem is not present when calling the function directly as in\r\n`foo(1)` or `foo(1, 2)`, since in this case, the number of arguments is\r\nknown. This resolution takes place in\r\n`runtime.FunctionCallSupport::findStaticMethodOrField`.\r\n\r\nAs a side note, their is some logic duplication between\r\n`runtime.FunctionCallSupport::findStaticMethodOrField` and\r\n`Predefined::fun` (and probably also in the method call resolution).\r\n\r\n\r\n## Current problem\r\n\r\nHowever, the direct call resolution use the number of *arguments* used\r\non the call site, and try to match them with the number of *parameters*\r\nof the candidate function.\r\n\r\nThis raise an issue when the function is overloaded with a variable\r\narity that shadow the fixed one. For instance, given the functions:\r\n\r\n    function foo = |x| -> ...\r\n\r\n    function foo = |x, y| -> ...\r\n\r\n    function foo = |xs...| -> ...\r\n\r\n* when using `fun`, the third one shadows the first one, and thus\r\n  `fun(\"foo\", mod.class, 1)` throws an exception;\r\n* when doing a direct call, the third one shadows the two others,\r\n  depending on the number of arguments given: `foo(1)` can call the\r\n  first or the third, and `fun(1, 2)` can call the second or the third.\r\n\r\n\r\n## Some tests\r\n\r\ngiven\r\n```golo\r\nmodule Test\r\n\r\nfunction foo = -> \"0\"\r\nfunction foo = |x| -> \"1\"\r\nfunction foo = |x, y| -> \"2\"\r\nfunction foo = |x...| -> \"1v\"\r\nfunction foo = |x, y...| -> \"2v\"\r\n\r\nfunction test = |msg, fn| {\r\n    try {                  \r\n    println(msg + fn())\r\n  } catch (e) {\r\n    println(msg + \"failed with \" + e: getClass(): getName())\r\n  }\r\n\r\n}\r\n \r\nfunction main = |args| {\r\n\r\n  println(\"# run\")\r\n\r\n\r\n  # can be 0 or 1v\r\n  test(\"direct 0: \", -> foo())\r\n\r\n  # can be 1 or 1v or 2v\r\n  test(\"direct 1: \", -> foo(1))\r\n\r\n  # can be 2 or 1v or 2v\r\n  test(\"direct 2: \", -> foo(1, 2))\r\n\r\n  # can be 1v or 2v\r\n  test(\"direct 3: \", -> foo(1, 2, 3))\r\n\r\n  # ok, always 0\r\n  test(\"ref 0: \", -> fun(\"foo\", Test.class, 0)())\r\n\r\n  # can be 1 or 1v\r\n  test(\"ref -1: \", -> fun(\"foo\", Test.class, -1)(1))\r\n\r\n  # can be 1 or 1v\r\n  test(\"ref 1: \", -> fun(\"foo\", Test.class, 1)(1))\r\n\r\n  # can be 2 or 2v\r\n  test(\"ref 2: \", -> fun(\"foo\", Test.class, 2)(1, 2))\r\n}\r\n```\r\n\r\n```bash\r\n#!/bin/bash\r\nfor i in `seq $1` ; do\r\n    golo golo --files test.golo\r\ndone | sort | uniq -c\r\n```\r\n\r\nThis gave me:\r\n\r\n    $ ./test.sh 1000\r\n        379 direct 0: 0\r\n        621 direct 0: 1v\r\n        741 direct 1: 1\r\n        151 direct 1: 1v\r\n        108 direct 1: 2v\r\n         93 direct 2: 1v\r\n        799 direct 2: 2\r\n        108 direct 2: 2v\r\n        784 direct 3: 1v\r\n        216 direct 3: 2v\r\n       1000 ref 0: 0\r\n       1000 ref -1: failed with AmbiguousFunctionReferenceException\r\n       1000 ref 1: failed with AmbiguousFunctionReferenceException\r\n       1000 ref 2: failed with AmbiguousFunctionReferenceException\r\n       1000 # run\r\n\r\nThis is not just a corner case!\r\n\r\n## Solutions\r\n\r\nNote that the two issues, though related, are not exactly the same, and\r\nthus a different solution can be adopted for direct call and function\r\nreference.\r\n\r\n### Explicit specification\r\n\r\nAdd a parameter to `fun` to specify if we want variable or fixed arity.\r\nThis would work when getting a function reference, but how do we resolve\r\ndirect calls?\r\n\r\n### Multiple references proxy\r\n\r\nAlso only in the case of function references (e.g. `fun` with no arity),\r\nwe could return a proxy containing all the ambiguous references instead\r\nof throwing a runtime exception. The resolution can then be done on the\r\ncall site, in a manner similar to direct calls.\r\n\r\nThis does not solve the issue of ambiguous direct calls.\r\n\r\n### Implicit choice\r\n\r\nDefine a priority between functions, for instance when multiple choices\r\namong fixed and variable arity, always use the fixed one. \r\n\r\nOptionally also define a priority when referencing with `fun` without\r\narity instead of raising a runtime exception.\r\n\r\nI’m not found of this solution. Their will surely be cases where the\r\nwrong function is used, so we need a way to be more explicit.\r\n\r\n### Runtime exception\r\n\r\nAs for the `fun` function, throw an `AmbiguousFunctionCallException` in\r\n`FunctionCallSupport` if the function is not fully defined. This does\r\nnot really solve the problem, but at least it does not pass unnoticed.\r\n\r\n### Compile time exception\r\n\r\nRefuse to guess, and throw a compilation exception if the developer has\r\ndefined ambiguous functions, where a variable arity one can shadow\r\na fixed one, forcing the developer to rethink its API.\r\n"},"comment":{"url":"https://api.github.com/repos/eclipse/golo-lang/issues/comments/164448896","html_url":"https://github.com/eclipse/golo-lang/issues/326#issuecomment-164448896","issue_url":"https://api.github.com/repos/eclipse/golo-lang/issues/326","id":164448896,"user":{"login":"yloiseau","id":7151021,"avatar_url":"https://avatars.githubusercontent.com/u/7151021?v=3","gravatar_id":"","url":"https://api.github.com/users/yloiseau","html_url":"https://github.com/yloiseau","followers_url":"https://api.github.com/users/yloiseau/followers","following_url":"https://api.github.com/users/yloiseau/following{/other_user}","gists_url":"https://api.github.com/users/yloiseau/gists{/gist_id}","starred_url":"https://api.github.com/users/yloiseau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yloiseau/subscriptions","organizations_url":"https://api.github.com/users/yloiseau/orgs","repos_url":"https://api.github.com/users/yloiseau/repos","events_url":"https://api.github.com/users/yloiseau/events{/privacy}","received_events_url":"https://api.github.com/users/yloiseau/received_events","type":"User","site_admin":false},"created_at":"2015-12-14T14:18:14Z","updated_at":"2015-12-14T14:18:14Z","body":"This is not working by far"}}