[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