[Gd-hackers] Required args and congruency (was Re: gtk interface: shorten identifiers, ... ?)
Chris Page
chris at chris-page.org
Tue Dec 5 12:35:18 CET 2006
On Dec 3, 2006, at 16:59 PM, Bruce Hoult wrote:
> On 12/4/06, Chris Page <chris at chris-page.org> wrote:
>> I've often thought that we should consider either adding
>> overloading--meaning that different numbers of required args would
>> simply create different generic functions with the same user-
>> visible name
>
> How on earth would the compiler know which generic function to use?
The same way it works in some other languages. The function name plus
the supplied arguments determine which actual (generic) function is
being called. But it's primarily a conceptual distinction whether you
consider it to be one generic function or several, the semantics
could be defined to be the same (or different, if that would offer
some advantage).
>> --or allow methods to require different numbers of required args
>> as long as they require at least as many as the generic function,
>> which must accept #rest, e.g.:
>>
>> define generic foo(req1, req2, #rest optional);
>> define method foo(req1, req2, #rest optional) ... end;
>> define method foo(req1, req2, req3, #rest optional) ... end;
>> define method foo(req1, req2, req3, req4, #rest optional) ...
>> end;
>>
>> These would be considered congruent and the number of arguments
>> passed in would be considered when dispatching.
>
> Doesn't seem very clean ...
In what way? Today you have to distinguish with explicit generic
functions with different names, which seems very awkward to me, e.g.:
define generic foo-1(req1);
define generic foo-2(req1, req2);
define generic foo-3(req1, req2, req3);
define generic foo-4(req1, req2, req3, req4);
The OpenGL API provides an example of how noisy this can make the
code. For some cases, turning them into #rest or optional keyword
arguments doesn't help much, as they don't participate in method
dispatch and they introduce a layer of overhead in which, say, a
method on foo(req1, #rest others) has to explicitly check the number
of arguments and apply() the appropriate GF. At the very least, I
would expect integrating this idiom into the (compile-time and
runtime) dispatcher could be more efficient.
> ... and I really don't see how you'd implement it to be fast unless
> the compiler exmapded it out to different GFs internally depending
> on how many arguments were passed, with the functions with fewer
> "required" arguments having implicit <object> specializations in
> the GFs for 3 or 4 arguments.
Something like that, which I expect is about the same as dispatching
on the other arguments. Consider this example:
define generic foo(arg-count, req1, req2, #rest optional);
define method foo(arg-count == 2, req1, req2, #rest optional) ...
end;
define method foo(arg-count == 3, req1, req2, #rest optional) ...
end;
define method foo(arg-count == 4, req1, req2, #rest optional) ...
end;
If the caller implicitly passed the number of supplied arguments to
arg-count, I should think this is about as efficient as dispatching
without that parameter (assuming the specializers of req1 and req2
are actually different in a real-world case). What's lacking here,
though, is the ability to also dispatch on the other argument types,
like so:
define method foo(req1, req2 :: <baz>, #rest optional) ... end;
define method foo(req1, req2 :: <bar>, #rest optional) ... end;
define method foo(req1, req2 :: <bar>, req3 :: <quux>, #rest
optional) ... end;
define method foo(req1, req2 :: <baz>, req3 :: <quux>, #rest
optional) ... end;
As a practical point, I think it would be cleaner to omit the
explicit #rest args for methods that handle a specific, fixed number
of arguments. In fact, it probably makes sense to introduce a new #-
word to indicate parameters whose types and count participate in
dispatch:
define generic foo(req1, #optional req2, req3, req4);
define method foo(req1) ... end;
define method foo(req1, req2) ... end;
define method foo(req1, req2, req3) ... end;
define method foo(req1, req2, req3, req4) ... end;
and then you can write:
define method foo(req1 :: <integer>) ... end;
define method foo(req1 :: <sequence>) ... end;
define method foo(req1 :: <integer>, req2 :: <integer>) ... end;
define method foo(req1 :: <integer>, req2 :: <sequence>) ... end;
and so on.
> This could be done with macros right now.
I'd love to see a rough outline of the macro call syntax and what the
implementation would look like. Can you elaborate? How would you
implement it, generally speaking? Would what you are imagining
require the number of arguments to be known statically? Would it work
with apply()?
--
Chris Page - Retrospective Electrical Engineer
Never attach a 12V/1A power supply to a 3V/5mA chip;
that releases the magic smoke that makes it go.
More information about the hackers
mailing list