On Mon, Jan 31, 2011 at 9:10 PM, Gary Wright <gwtmp01 / mac.com> wrote:
>
> On Jan 31, 2011, at 2:47 PM, Charles Oliver Nutter wrote:
>
>> On Mon, Jan 31, 2011 at 1:27 PM, Thomas E Enebo <tom.enebo / gmail.com> wr=
ote:
>>> On Mon, Jan 31, 2011 at 9:00 AM, Gary Wright <gwtmp01 / mac.com> wrote:
>>>> I think the original poster provided an example of overloading based o=
n the number of parameters but not their type.
>>>> Even restricting yourself to overloading by arity is a bit problematic=
 in Ruby because the arity still has to be determined (in some cases) dynam=
ically:
>>> Actually, arity of callsite is always calculated =A0in Ruby to know if
>>> you should throw an ArgumentError (3 for 0 specified sort of errors)
>
> Maybe I'm overlooking something but I wasn't suggesting that you don't ne=
ed to calculate arity but instead was pointing out that you can't just calc=
ulate it at parse time but sometimes need to calculate it at call time.
>
> foo(1,2) =A0 =A0 # the call arity can be computed at parse time
> foo(*a) =A0 =A0 =A0# the call arity must be computed at call time
>
> So in an 'overload-based-on-arity' scheme there might be some optimizatio=
n opportunities for some call sites but not for every call site.

Yeah, that is certainly true in the general sense even in existing
Ruby semantics.  In your first example, we know that it is always a
two-arg call (at parse time) so we can go through a two-arity call
path and not 'box' the call parameters into an array.  In the second
case we might be able to know arity at parse time if we know 'a' is a
literal array.    If we don't then the optimizations we can do get
more limited.

You are correct that adding arity to the mix would make the
optimizations more complex, but I think it depends on the case and
what you get by supporting it.  For example, in the second case if we
made our callsite cache lookup the methods foo and cache all of them
at the site, then dispatch to the appropriate one, we would have quite
a bit more complicated callsite cache (since we would need to
invalidate it if any arity version changed), but this would dispatch
much faster than doing things like you are showing in the example
later in your email (actually much faster than any pure-Ruby logic for
arity resolution).  In the case where there was only one arity it
would behave more or less like it does currently.  The main change
would be on any same-named method we would need to invalidate.  [Note:
This is just one way this could be done and callsite invalidation
would be about the same as what it is now since it would invalidate
based on name.   We could do it name+arity.  We could do it both ways
even (perhaps name+arity for first case and only name for second).

With all this said, it seems like a good idea to me, but OTOH no
feature is without its caveats.  I am more worried about Ruby
programming style than performance and though it seems to pass a smell
test for me...I don't think I am in the 'yeah let's do it camp' yet.
I am probably in the 'someone should play with this' camp.

>
> Seems like you can get pretty far though with just a little meta-programm=
ing with no special language support:
>
> module Arity
> =A0def overload(base)
> =A0 =A0define_method(base) do |*args|
> =A0 =A0 =A0argc =3D args.size
> =A0 =A0 =A0method_name =3D "#{base}_#{argc}"
> =A0 =A0 =A0if respond_to?(method_name)
> =A0 =A0 =A0 =A0send(method_name, *args)
> =A0 =A0 =A0else
> =A0 =A0 =A0 =A0raise ArgumentError, "wrong number of arguments (no method=
 for #{argc} arguments)"
> =A0 =A0 =A0end
> =A0 =A0end
> =A0end
> end
>
> class A
> =A0extend Arity
>
> =A0overload :foo
>
> =A0def foo_0
> =A0 =A0puts "foo with no arguments"
> =A0end
>
> =A0def foo_1(arg1)
> =A0 =A0puts "foo with 1 argument: #{arg1.inspect}"
> =A0end
>
> end
>
> A.new.foo =A0 =A0 =A0 =A0 =A0 # dispatches to A#foo_0
> A.new.foo(1) =A0 =A0 =A0 =A0# dispatches to A#foo_1
> A.new.foo(1,2) =A0 =A0 =A0# ArgumentError
>
>
> Gary Wright
>
>



--=20
blog: http://blog.enebo.com=A0 =A0 =A0=A0 twitter: tom_enebo
mail: tom.enebo / gmail.com