Last update October 24, 2006

Keyword Args



Difference (last change) (Author, normal page display)

Changed: 163,164c163

In Lisp...
Python allows arguments to be specified (optionally) by keyword, but doesn't allow overloading. (I.e. functions cannot be overloaded by type or number as they can in D.)

Changed: 167,170c166,170
Most of the issues are unresolved at this time. <g>

The main issue that comes to mind is that matching overloaded versions of functions becomes more complicated to implement.
But I don't think it's impossible.
* Function name mangling would have to be extended to include the keyword names, as this info would have to be present in compiled libs.
* More thinking needs to be done about how this would interfere with or complicate the matching process used to deduce which version of an overloaded function to call.
* The practice of using no-name arguments in declarations or for unused parameters may cause conflicts ( i.e. currently it is ok to define a function "void foo(int,int) {}" if you don't need to use the parameters in the body. Similarly it's ok to have "void foo{int,int);" in a header file and "void foo(int a, int b){}" as the actual definition.
* For method overrides, do the argument names have to match the ones used in the parent class? What if they don't?
* Since declarations can be extracted from a D source file to create a sort of header file, some conflict resolution policy would have to be decided upon if the declaration in a header file differed from the actual definition.

Deleted: 172d171
Also the C/C++ practice of no-name arguments in types may cause conflicts.

Keyword Arguments

A proposal for allowing named keyword arguments for functions and templates.

The Issue

The 'C++' style of function and template arguments inherited by D has some limitations.

  • Having to provide many unnecessary defaults
  • Unclear meaning of arguments at call time
  • Flexibility and maintainability of code when argument lists are changed
  • Difficulty of rememebering order of seldom-used arguments

Too many defaults

Functions and templates can sometimes grow a lot of arguments. Currently you can specify an argument list with defaults like:

void aFunction(
    t1 arg1,
    t2 arg2,
    t3 arg3 = a3_default,
    t4 arg4 = a4_default,
    t5 arg5 = a5_default
);

You can then call aFunction leaving out the last few arguments, and default values will be supplied. Unfortunately, you can't just supply arg4 without also supplying arg3. And it's up to you to remember the order of all the arguments. This problem is often seen to its extreme in GUI toolkits, where widget creation functions take many many parameters.

If you could call using keywords or named arguments like in some other programming languages (e.g. Python, Lisp) then the problem would be solved:

    aFunction(a, b, arg4:17)

Here's a typical example of a constructor from wxWidgets:

wxStatusBar(
    wxWindow *parent,
    wxWindowID winid,
    const wxPoint& pos = wxDefaultPosition,
    const wxSize& size = wxDefaultSize,
    long style = wxFULL_REPAINT_ON_RESIZE,
    const wxString& name = wxPanelNameStr);

To specify the name, for instance, you have to supply 3 additional unrelated arguments: pos,size, and style.

So you have to do:

new wxStatusBar(parent, id, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE, "the name");

Instead of the much clearer and more maintainable alternative possible with keyword arguments:

new wxStatusBar(parent, id, name:"the name");

Unclear meaning

Lack of readability at the place of call is another problem. Consider a function with one or more boolean arguments. It is common to make a function with a few bool paramters like:

void manipulate(Size scale, bool lock_x=false, bool lock_y=false);

This may not be considered good practice, but it's very easy to do, so people are going to do it one way or another. The problem is that at the point of call you have something like:

manipulate(scale, false, true);

and then you have to go back to the definition of the method to see what false and true mean there.

If you want to have meaningful boolean parameters you can define some enums like

LOCK_ON=true
LOCK_OFF=false

but that's extra work, and also it introduces more symbols into the namespace. Usually to prevent clashes such enums get names like:

QB_MANIP_WIDGET_LOCK_ON=true
QB_MANIP_WIDGET_LOCK_OFF=false

So the call ends up looking very ugly, like:

manipulate(scale, QB_MANIP_WIDGET_LOCK_OFF, QB_MANIP_WIDGET_LOCK_ON)

And even then its STILL not clear looking at it what's being locked. So you could introduce even more enums like {LOCK X ON}? {LOCK X OFF}?, and use those. But then (if you make them real enums) you're introducing several new enum types. Wow that's getting complicated. We've introduce 2 public enum types with 2 public values each just to be able to call this silly function.

On the other hand, if you have named keyword arguments then you can just call it like:

manipulate(scale, lock_y = true)

The above code is perfectly clear to read, and no silly enums were required.

Maintainability

But now suppose we want to add another argument in before lock_x, and lock_y. We can still do it without messing up existing code:

void manipulate(Size scale, Observer obs=None, lock_x=false, lock_y=false)

Then our manipulate(scale, lock_y=true) call is still valid.

Not Having to Remember Order

It's easy to forget the proper order for arguments. Was it set_item(index,value) or set_item(value,index)? With keyword arguments you don't have to rememeber.

   set_item(value="hi", index=3)

You just have to remember their names. Which is sometimes easier if a library uses consistent naming conventions.

Templates

All of the above also goes for specifying template parameters too.

Syntax

There are two clear candidates

   aFunction(1,2,key=5)

or

   aFunction(1,2,key:5)

The first one is reminiscent of default parameter declarations:

   void aFunction(int arg1, int arg2, int key=5);

The second one is reminiscent of structure initializers:

   static X z = { c:4, b:5, a:2 , d:5};  // z.a = 2, z.b = 5, z.c = 4, z.d = 5

I prefer the first because it looks more intuitive to me, but the second is probably a closer semantic match with setting argument values via keywords.

Ideally I'd like static array initializers to change to c=4 type syntax.

Summary of Keyword Arguments in other Languages

In Python: from the Python Tutorial Python allows arguments to be specified (optionally) by keyword, but doesn't allow overloading. (I.e. functions cannot be overloaded by type or number as they can in D.)

Unresolved Issues

  • Function name mangling would have to be extended to include the keyword names, as this info would have to be present in compiled libs.
  • More thinking needs to be done about how this would interfere with or complicate the matching process used to deduce which version of an overloaded function to call.
  • The practice of using no-name arguments in declarations or for unused parameters may cause conflicts ( i.e. currently it is ok to define a function "void foo(int,int) {}" if you don't need to use the parameters in the body. Similarly it's ok to have "void foo{int,int);" in a header file and "void foo(int a, int b){}" as the actual definition.
  • For method overrides, do the argument names have to match the ones used in the parent class? What if they don't?
  • Since declarations can be extracted from a D source file to create a sort of header file, some conflict resolution policy would have to be decided upon if the declaration in a header file differed from the actual definition.

FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: October 24, 2006 8:24 (diff))