Declaring arguments to a function

Hans-Dieter.Dreier@materna.de Hans-Dieter.Dreier@materna.de
Mon, 12 Jul 1999 17:33:10 +0200


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

>Hans-Dieter.Dreier@materna.de wrote:
>
>
>> Let me state some observations from everyday 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
>operators.
>
>
>I am not certain that I understand what you mean by "chained infix
>operators", could you give me
>an example?

cout << "The result is " << i << "." << endl

... or, to chain strings:

str =3D "The result is " + NumToStr (i) + "." + CrLf

... or, to build a list:

list =3D a, b, c

(Here the "," operator forms a new list from two simple values or appends a=
 value to a list).

>> 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.
>
>This could be so, but if the argument number rarely creeps above 6 then
>there is not much difference. My feeling was that by
>splitting the seperate aspects of the declaration into its constituent
>parts that the user could focus on any one of those parts by looking on
>the particular line that applies to that aspect. The conventional way
>requires the user to read across and
>down scanning each argument declaration in turn, having to read
>everything until what is looked for is found. If I write
>
>[Name-JeremyDunn : BirthYear-1957 : HomeTown-Bellingham,
>Name-HarryHoudini : BirthYear-1957 : HomeTown-NewYork]
>
>I could also write this as
>
>[Name(JeremyDunn,HarryHoudini),
> BirthYear(1957),
> HomeTown(Bellingham,NewYork)]
>
>There are some advantages to doing it this way, as one can concentrate
>on the category of information quickly. This particular case is more
>readable than the first method and displays the information in a more
>meaningful manner. The lack of indice on 1957 would have the default
>meaning of applying to all the Names and the HomeTowns don't need to be
>indiced because they equal the number of Names and correspond directly
>to them. Indicing need only occur when the position of the item in the
>given list does not correspond with the same indice in the Names list.

If I forget to specify a value, I'd prefer to get a warning, even if that m=
eans that I have to write a little more. Maybe it is simply a matter of per=
sonal preferences. How about allowing both forms, alternatively ?

>> > 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?
>
>
>Not often. You know, I have a spare tire in my car that I have never had
>to use but I think I'll keep it
>there anyhow.

Nice analogy. Actually, I think spare tires are superfluous (if you keep to=
 populated areas), so we'd save a lot of gas if we'd remove them. How about=
 including a backup assembler to the compiler, in case the compiler breaks =
down?
(Just kidding).

>> >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?
>
>
>No, the EmptyArguments() function is used INSIDE a function to gather
>information about what has actually been
>supplied to the function. You could write something like
>
>FUNCTION(a,b,c,d){
>   Z =3D EmptyArguments()
>   If Member(3,Z) Then <do steps>
>}
>
>If the user now uses this function and writes FUNCTION(a,b,c,) where d
>is missing then EmptyArguments() will return a list (3) with the indice
>of the argument that is there but empty. The function will then compare
>3 to the list Z and if 3 is in the
>list then a series of steps will be performed. EmptyArguments() enables
>the programmer to easily determine what is not there and respond to it.

I see. I thought you wanted to supply default values from "the previous fun=
ction statement" which I misunderstood as "the previous function call".

Why not check the argument directly, like this:

FUNCTION(a,b,c,d) {
   If IsEmpty(d) Then <do steps>
}

Isn't that shorter? And you don't need to count arguments. Actually, names =
were invented for that purpose.

>> >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
>like arrays and structures, with modest extra effort. No additional rules
>required!
>
>
>Perhaps so, but how modest is this extra effort? Why have the extra
>effort in the first place if you can do the task
>in a more direct manner?

Well, see example above. Isn't that more "direct" (ie shorter and clearer) =
than your approach?
>
>
>> >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
>rules. It is not self-explanatory (at least not to me). Sorry, but "2(1-)"
>somehow looks like a syntax error (and in another context, it would be one=
).
>
>
>Right, not intuitive, just different. Additional rules? No more so than
>normal languages, look in your standard textbook at
>how much space is devoted to explaining optional arguments, variable
>number of arguments, passing arrays etc. I don't think
>this involves EXTRA rules, just different rules.

I would not allow variable numbers of arguments: use lists instead. I would=
 not allow optional arguments: Either have a "skip" value (similar to C-NUL=
L pointers, but potentially usable for any type), or -better still, because=
 it requires no extra rule- somehow derive all potentially-optional values =
from a (predefined) singleton class that serves the purpose of supplying a =
"skip" value which can be tested for.

>> IMHO it is important to keep the structure of the language simple. The b=
est
>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 each
>other without having to look at the context. Ideally, the same token would
>always stand for the same functionality, regardless of where it would be u=
sed.
>Example:
>> =

>> For a range, I'd write 1..2 instead of 1-2, because the meaning of "1-2"
>depends 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 funct=
ion
>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 (ba=
d)
>example IMO is the use of <> for template arguments in C++. I'd *never* do=
 it
>this way.
>> =

>
>
>Your first paragraph here gives me DejaVu Dieter. I recall making the
>argument for completely consistent syntax but everyone
>balked when I suggested applying it to ALL operators including unary
>ones like +-*/. The reason? They just don't like it. It reminds me of
>the American attitude about the metric system, they don't want to get
>rid of those silly feet and inches. Anything short of what I suggested
>automatically depends on context! How can you write pow(a,b) and in the
>next line write a+b rather than +(a,b) and not use context to tell what
>is going on? That is one reason I don't like most languages, most them
>have "special" layouts for certain functions but not others, and each
>time they do this requires the user to remember yet another exception to
>what could be a completely consistent grammar. I have often found it
>interesting that out of the hundreds of languages that have been written
>that LISP is probably the only language that is grammatically
>consistent. There are only 3 grammars possible:
>
>x FUNC y    unary operators (inherently limited, requires precedence
>rules)
>FUNC(x,y)   works fine-no precedence
>(FUNC,x,y)  works fine-no precedence

Why do you call +-*/ "unary" operators? They require two operands, therefor=
e they should be named "binary" or "infix" operators. How do you call opera=
tors that take just one argument, like "-" (to take the negative value of a=
 number)?

>If we want the minimum of context rules we are forced to pick one of the
>last two forms and carry it out without exception.
>Oh well, I am wasting my time. Human nature is against me, people want
>feet and inches and the Julian calendar. Enough ranting for today!
>

Maybe the truth lies in the middle. Certainly, LISP has a very simple synta=
x, which is good, but it pays dearly by requiring a pretty printer just to =
be readable. Taken to extremes, we could do with just two symbols, 0 and 1,=
 and encode everything binary, or we could go the APL way and invent a spec=
ial character set which has a special symbol for each keyword (very short b=
ut, again, hard to read). IMO it is not by chance that most "main stream" p=
rogramming languages nowadays look so similar to each other. It's the same =
as with bicycles or pencils: A mature design is hardly improvable unless do=
ne in a completely new way (say, programming by data glove).

Regarding the syntactic handling of special (ie operator) symbols like ordi=
nary names: You are right, in principle, BUT if you also allow the "traditi=
onal" form, the syntax tends to get too permissive, allowing too many typos=
 to form legal (but nonsensical) expressions. For this reason, it may also =
be harder to read by humans. And if you don't have traditional infix operat=
ors, you end up with LISP-like syntax.

IMO questions of program representation are not so important if the *struct=
ure* and the *concepts* of a language are well designed. Remember, one of t=
he goals of Ultra is to allow for *different* front-ends: LISP-like or APL-=
like or C-like: no problem as long as there is a transformation to internal=
 repesentation, which must be chosen to be as flexible as possible (AST). A=
nd of course, there must be runtime support for the services needed by the =
language, but that is exchangeable and extendable, if done right.

So, let's not talk about syntax. Let's talk about language structure and co=
ncepts and the foundations on which they build: memory management and execu=
tion mechanism, and not worry about representation.


--

Regards,

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

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

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

--4aSfCsv40c4jB8WqLVgxaFPDyTsqvpNo--