History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: BOO-250
Type: Bug Bug
Status: Reopened Reopened
Priority: Major Major
Assignee: Rodrigo B. de Oliveira
Reporter: Arron Washington
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Boo

overloaded functions cannot be "first class functions"

Created: 11/Feb/05 03:21 PM   Updated: 20/May/08 09:09 AM
Component/s: Compiler
Affects Version/s: 0.5
Fix Version/s: 0.8.3

Time Tracking:
Not Specified

Environment: Latest SVN.
Issue Links:
Related
 


 Description  « Hide
Functions that are overloaded (such as double.Parse and string.Format) cannot be used as first class functions because the compiler cannot resolve to any one of the overloaded functions.

 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Arron Washington - 07/Apr/05 12:15 AM
I looked into this issue at some depth and realized that it fails for all functions that are overloaded because the compiler can't determine which of the overloaded functions to resolve and bind to.

That changes the entire scope of the issue.


Doug H - 07/Apr/05 11:00 AM
This doesn't fix the issue at all but another workaround is to use a generator expression instead of map() + a closure: http://boo.codehaus.org/Generator+Expressions

return array(double, double.Parse for n in numbers)


Rodrigo B. de Oliveira - 07/Apr/05 11:09 AM
By the way, the compiler is able to tell the array type by its own

try this:

value = array(double.Parse(value) for value in ("1.0", "3.15"))[-1]


Arron Washington - 07/Apr/05 12:04 PM
Yes, that's a good workaround for some cases more than others.

(I can't make "def Apply(function as callable, value as duck):
/* manipulate value here */
return function(value)" use anything that is overloaded, for instance).

I don't suppose there is a way to delay looking up overloaded functions until they've been invoked?


Bill Wood - 07/Apr/05 05:26 PM
Take a look at the InvokeMember function - you may be able to use it for your example (I don't have time to delve into it right now). See http://docs.codehaus.org/pages/viewpage.action?pageId=13653 for an example of InvokeMember in action.

Doug H - 19/May/05 09:30 PM
This doesn't implement this feature, but it fixes a related compiler exception.
This line of boo code will cause the compiler to throw an exception:
f = double.Parse

To change it to an ambiguous reference error instead, change line 2266 of ProcessMethodBodies from this:

if (NodeType.ReferenceExpression == node.NodeType &&

to this:

if (node is ReferenceExpression &&

This fixes the problem because double.Parse for example is a memberreferenceexpression, which is a subclass of referenceexpression.


Doug H - 20/May/05 01:17 AM
I didn't catch that events weren't working, such as: AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve
because an event is a memberreferenceexpression that is still ambiguous internally (field + event of same name)
at the time PostProcessReferenceExpression is called.

So this version makes exceptions for events:

if (node is ReferenceExpression &&
!AstUtil.IsTargetOfMethodInvocation(node) &&
!(EntityType.Field == amb.Entities[0].EntityType &&
EntityType.Event == amb.Entities[1].EntityType))


Rodrigo B. de Oliveira - 22/Jun/05 03:13 PM
Thanks Doug!

Doug H - 24/Sep/05 01:22 PM
Perhaps the compiler could create a method that dispatches to the right overload at runtime, like below. Otherwise, the ironpython solution is to specify the types in brackets after the method name like so, but I would have thought that this would conflict with generics: r = System.Random().Next[int](10,20)

def __parse(*args): //method created by compiler
l = len(args)
if l==1 and args[0] isa string:
return double.Parse(cast(string,args[0]))
elif l==2 and args[0] isa string and args[1] isa System.IFormatProvider:
return double.Parse(cast(string,args[0]),cast(System.IFormatProvider,args[1]))
elif l==2 and args[0] isa string and args[1] isa System.Globalization.NumberStyles:
return double.Parse(cast(string,args[0]),cast(System.Globalization.NumberStyles,args[1]))

d = __parse //replacement for double.Parse

print d("34.3")


Doug H - 29/Sep/05 09:51 PM
The solution I proposed in last comment wouldn't really work well with instance methods, only static methods. Here's a way that would work with instance or static methods by creating an ICallable class, but I don't know if the speed hit is so bad as to make this option undesirable:

class A:
def test():
print "test1"
def test(a as int):
print "test2", a
def test(a as int, b as int):
print "test3", a, b

//This is created automatically by the compiler:
class _A_test_Callable(ICallable):
_inst as A
def constructor(inst as A):
_inst = inst
def Call(*args):
l = len(args)
if l==0:
_inst.test()
elif l==1 and args[0] isa int:
_inst.test(cast(int,args[0]))
elif l==2 and args[0] isa int and args[1] isa int:
_inst.test(cast(int,args[0]),cast(int,args[1]))
else:
raise "invalid arguments"

a = A()
//c = a.test //won't work, boo-250
c = _A_test_Callable(a) //instead compiler inserts this as replacement for "a.test"
c(3,4)
c(1)
c()


Rodrigo B. de Oliveira - 30/Sep/05 02:44 PM
this sounds interesting, specially as a way to enable multimethods:

class Foo:
def bar(a as int):
print a
def bar(a as string);
print a

a = functionReturningObject()
f = Foo()
// today
(f as duck).bar(a)

// with your proposal
(f.bar)(a)

interesting...


Doug H - 30/Sep/05 07:25 PM
I didn't notice if we use ICallable, we lose the return type since ICallable.Call returns object. So here's a version that just uses a regular class with an Invoke method that does the dispatching:

class A:
def test():
print "test1"
return 1
def test(a as int):
print "test2", a
return 2
def test(a as int, b as int):
print "test3", a, b
return 3

//This class is created automatically by the compiler:
final class _A_test:
_inst as A
def constructor(inst as A):
_inst = inst
def Invoke(*args) as int:
l = len(args)
if l==0:
return _inst.test()
elif l==1 and args[0] isa int:
return _inst.test(cast(int,args[0]))
elif l==2 and args[0] isa int and args[1] isa int:
return _inst.test(cast(int,args[0]),cast(int,args[1]))
else:
raise "invalid arguments"

a = A()
//c = a.test //won't work, boo-250
c = _A_test(a).Invoke
print c(3,4) * 2 //I do a "*2" here to check that return type is preserved
print c(1) * 2
print c() * 2


Avishay Lavie - 11/Jun/07 11:27 PM
Isn't Doug's last comment exactly what's being done currently, with the dynamic dispatcher thingy?