Declaring arguments to a function

Hans-Dieter.Dreier@materna.de Hans-Dieter.Dreier@materna.de
Thu, 17 Jun 1999 09:24:00 +0200


--IEIdeOlgaueLxkgaz7wZ8HxzyRwiZdJm
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable


>We wish to do the following in defining arguments:
>
>1. We want to define the number of arguments.
>2. We want to define the type of the arguments.
>3. We want to define default values if the argument position is present
>but empty.
>4. We want to define default values if the argument position is not
>present.

Let me state some more goals:

5. We want a syntax that does not invite programmer mistakes.
6. We want a syntax that is concise, so that programs are easy to read.
7. We want the compiler to catch as many argument matching errors as possib=
le.

>
>I propose a form something like
>
>return-type FUNCTION NAME
>        (<# of arguments>,
>         <argument names>,
>         <argument types>,
>         <default values> (if any)
>        )
>{
>  <program statements>
>}
>
>Let us discuss each part of the declaration one step at a time. I
>envision the number of arguments as  being declared as an integer as
>one
>would declare a string array in C where you must decide on a value. But
>what if I define 20 arguments and I need to put in 30? I will get to
>that later.

Let me state some observations from every day programming:

1. Most functions have a fixed small number of arguments.
Few functions have a fixed large (> 5) number of arguments.
Very few functions (at least in C) have a variable number of arguments.
Most of them are I/O, and there is an alternative: use of chained infix ope=
rators.

2. The more arguments a function has, the greater the opportunity to make a=
 mistake in the parameter list: If the function has 2 arguments but you sup=
ply only one, you will see the difference immediately when lloking at the s=
ource code (provided that the expression supplied is not a big ugly monster=
). If the function has 6 arguments but you supply 5, you won't notice the d=
ifference unless you count. Therefore it is less error-prone for a function=
 to have a small number of arguments. I prefer 0-2 arguments.

3. If you have a list of tuples, the form (A a, B b, C c, ...) is easier to=
 read and write than the form (A B C, a b c) if the list is long. The secon=
d form invites mismatches.


>
>Declaring argument names is done by giving the name of the argument you
>are going to use in your program and then following it by the index of
>the argument position that is going to use that name.

Counting is additional overhead and yet another possibility to make mistake=
s.
Why not use the argument name, something like

Foo (MySecondArg =3D 23, MyFirstArg =3D 4)


> For instance, in a
>function of two arguments we could write either (Base,Power) without
>indexes if we have a name for every argument or we could write it as
>(Base(0),Power(1)) where we declare the indice.

Can't this be mistaken for a function call (same syntax)?
Of course, the same applies for my example: A syntax will have to be chosen=
 that does not occur in normal expressions.

> Suppose the 1st argument
>is named Base but we have several related arguments following it of the
>same type in our program? We could write (Base(0),Power(1-)) where the
>hyphen means that all arguments following index 0 are related somehow.
>The compiler would automatically number these extra argument names as
>Power1, Power2 and so on, and that is how we would call them in our
>program.

Nice, but how often do you need such a feature?

>
>Defining the argument types is done in a list like (int, float, string)
>and so on for each argument. If all the arguments are of the same type
>then you could write (int) to have the type default to int for all of
>them. Suppose the first three arguments are type int and everything
>following those are type string? You would write that as
>(int(-2),string(3-)) where the statement means that arguments at list
>indexes from 0 to 2 are ints and the indexes from 3 onwards are of type
>string. Nonconsecutive indexes of the same type could be stated like
>(int(0,2,4),string(1,3)). (Simple eh?

It gives me the odd feeling that you are calling functions. =


>Default values are equivalent to what one does with the "optional"
>statement in VB. In this case one declares the optional value and
>follows it with a bracketed list of the index values of the arguments
>it
>applies to. The index values for the arguments are numbered starting at
>0. So if I wanted to declare that argument 3 defaults to 24 then I
>would
>write the statement (24(23)). If I wanted arguments 1 thru 4 to default
>to "A" and argument 5 onward to default to 2 then I would write
>("A"(-3),2(5-)). I could also have written the start and end indexes
>explcitly as in ("A"(0-3),2(5-)). If the start index is missing it
>assumes that it is 0 and if the finish index is missing then it
>continues for all arguments after the first one. Naturally, one can
>choose nonconsecutive indexes as in a statement like ("A"(0,2,7)). One
>may not declare more than one optional value for a given index.

All very nice, but again, how often does one use it?

>Now we have three special commands in our language that are very useful
>in regards to all of this. These three functions have no arguments but
>return information that is very useful. The 1st function
>NumberOfArguments() basically counts the number of commas in the
>argument part of the function and adds one to give the total number of
>argument spaces that are defined by your statement. So if you wrote
>
>FUNCTION(,,,,)
>
>with nothing actually input into the statement then NumberOfArguments()
>would say there are 5 argument slots.
>
>The 2nd function is called EmptyArguments(). This function looks at the
>previous function statement and gives a list of the argument indices
>that have nothing in them. In our example the list (0,1,2,3,4) would be
>returned.

You would write FUNCTION (EmptyArguments ()), right?

>The 3rd function is the complement to the previous and is called
>FullArguments(). This function returns a list of all indices that
>actually had some characters typed into them.
>This way of doing things gives us full argument control and enables us
>to do some things programatically that cannot be done in other
>languages.

That is not quite true. You can always simulate this with standard means li=
ke arrays and structures, with modest extra effort. No additional rules req=
uired!

>How about an example? Let us write a function called Pwr() that returns
>the power of a number. Let the statement Pwr(s,t) be equivalent to the
>statement s^t. Let the function allow us to input up to 5 extra powers
>so that the statement Pwr(s,t,u,v,w,x) would be equivalent to
>(((((s^t)^u)^v)^w)^x). If the 1st argument "s" is empty then we wish
>the
>base to default to 2.718... the base of logarithms thus Pwr(,t) is
>equivalent to exp(t). If there is only argument then the function takes
>the square of whatever you put into it i.e. Pwr(x) is the same as x^2.
>If there are two arguments and the 2nd argument is empty then the power
>is assumed to be 3 i.e. Pwr(x,) is the same as x^3. If there is three
>or
>more arguments and any of the power arguments are empty then they are
>assumed to be 2. So the statement Pwr(s,,,) would be the same as
>(((s^2)^2)^2). We would write our function declaration like this:
>
>double Pwr(<7,
>           (Base(0),Power(1-)),
>           (double),
>           (2.718(0),2(1-)),
>          )
>{
>  <program statements>
>}
>Now using our special three functions we can access all the argument
>information we need to write a program that does all of the above.
>There
>is no way to write a function with ALL the features described without
>something like what I have described. I think my way is more intuitive,
>we do not have to deal with Paramarrays and such.

I don't think it is more intuitive: It requires you to learn additional rul=
es. It is not self-explanatory (at least not to me). Sorry, but "2(1-)" som=
ehow looks like a syntax error (and in another context, it would be one).

>What about if I write an addition function that has 20 arguments
>defined
>and I supply 30?

IMO you got your application design wrong in the first place if your functi=
on has 20 arguments. So I wouldn't bother supplying a feature which encoura=
ges bad programming style.

> We must come back to stating the number of arguments.
>If I write <7 as in the example then the compiler assumes that I mean
>that the function must have less than 7 arguments. If I wrote =3D6 or
>just
>6 instead then the compiler would assumed that I meant that the
>function
>must have 6 exactly 6 arguments present. In fact, we could even write
>((>0,<7)) to declare that the function must have at least one argument
>present but no more than 6. If the argument number statement is empty
>then it assumes that the function requires NO arguments. I cannot
>declare simply >0 to imply a function that holds any number of
>arguments
>but must declare some limit. If I say <7 and input 8 arguments then I
>may input extra arguments but in the program I must use
>NumberOfArguments() to test the number and if it is more than 6 I must
>issue the equivalent of a VB Redim statement. Let us say I actually
>input 10 arguments, I would have a statement like
>
>        N =3D NumberOfArguments()
>        If N > 6 {
>           ReDim Pwr(N)
>        }
>
>In this case the name of the array is the name of the function. All
>extra arguments will take on the name and qualities of the last
>argument
>defined in the original function. If we define the number of arguments
>as =3D6 or just 6.
>
>So, does anyone like any of this? Detest it? I await your insightful
>comments.


I'd suggest: Let's have a fixed number of arguments and the ability to pass=
 manifest (literal) lists to support variability, like this:

int: Foo (int: arg1, string: arg2 =3D "", list(int): arg2 =3D {1,2}) {...}

(arg2 defaults to "", arg3 defaults to the list {1,2}).

With a call like this:

Foo (3, "Hello", {1,2,3});

To support "missing" values in a generally usable way, I'd introduce a spec=
ial "skip" value (to be inserted for void operands) which can also be expli=
citly written as "?", like in Algol68. If in certain syntactical positions =
an operand would be missing, "skip" would be assumed. "skip" could be used =
for any data type, like NULL for pointers of any type in C++. Thus, you cou=
ld check for missing values like this:

if xyz =3D=3D ? then ...

Given this, we could even do without the default parameter mechanism.

IMHO it is important to keep the structure of the language simple. The best=
 way to do this is to have a small set of orthogonal constructions which wo=
rk the same everywhere. Furthermore, they should be distinguishable from ea=
ch other without having to look at the context. Ideally, the same token wou=
ld always stand for the same functionality, regardless of where it would be=
 used. Example:

For a range, I'd write 1..2 instead of 1-2, because the meaning of "1-2" de=
pends on the context (range 1-2 or numerical expression). Keeping track of =
the context adds a level of uncertainty to the language novice. =


For the same reason, I'd use () only for expression precedence and function=
 call. Most programmers are used to it, it is common practice and it works =
well. Using () for other purposes as well doesn't add to clarity. Best (bad=
) example IMO is the use of <> for template arguments in C++. I'd *never* d=
o it this way.


--

Regards,

Hans-Dieter Dreier
(Hans-Dieter.Dreier@materna.de)=

--IEIdeOlgaueLxkgaz7wZ8HxzyRwiZdJm
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable

IDENTIFIKATIONSANGABEN:
a23648a.txt IA5 DX-MAIL X.400 User Agent=

--IEIdeOlgaueLxkgaz7wZ8HxzyRwiZdJm--