www.digitalmars.com         C & C++   DMDScript  

D - Parameter passing

reply Antti Sykari <antti.sykari housemarque.fi> writes:
Current situation:
 - Integer values are passed by value
 - I assume that structs are passed by value as well, correct me 
   if I'm wrong
 - Objects are passed by reference
 - Array objects are passed by the infamous "something in between"

Another problem is:
 - Objects cannot be passed with "read-only" semantics
 - The same holds for arrays, I think

What we would ideally like is:
- The visible effect of passing parameters should be the same
  regardless of the type of object
- The user should be able to specify "constness" (C++-term) in
  function arguments

Right?

One solution could be:

Pass everything "const" by default. Then it doesn't matter whether the
parameters are passed by reference, value, or whatever. The compiler
may determine the most efficient calling convention. For example, it
could just pass everything in registers and the function would not
change their values. Or it could pass a pointer to stack or heap, if a
bigger object is passed. But still, the function should only be able
to call read-only ("const") functions of that class.

int f(int x, complex y, int z[])
{
        // x, y, z are passed in registers (for efficiency)

        // f may not change x, y, z or their contents, but can use
        // their values
}

int f(BigObject o)
{
        // o is passed by reference (implemented as passing a pointer)

        // still f could not change its value, only call it's
        // "const" functions
}

If function should be able to change its arguments, it should declare
it explicitly with a keyword. What keyword? I can't say. I use "inout"
here which might not be the best solution.

int f(BigObject x, BigObject y, inout BigObject z)
{
        // may not change x or y, but can change z
}

Or maybe it could use a different syntax like:

int f(BigObject x, BigObject y, BigObject! z)
{
        // may not change x or y, but can change z
}

This would be D equivalent of the C++ function:

int f(const BigObject& x, const BigObject& y, BigObject& z) { .. }

but IMHO looks simpler and takes the attention to where it belongs: in
the z variable, which is in the danger of being modified by the
function.

Naturally, if function _could_ change its arguments, they should be
passed by reference, whether they are integral types, objects, arrays,
or structs.

The downside with this scheme is that D doesn't currently support the
concept of "read-only object". Now, I don't like the "const" keyword
in function declarations, since it already means something different
(namely, the usage like "const int x_size = 640;"). However, now that
we have properties, maybe objects could have a context-dependent
property "readOnly":

f(BigObject o)
{
        assert(o.readOnly == true);
}

g(BigObject! o)
{
        assert(o.readOnly == false);
}

Similarly, member functions should have this property as well, and it
should be visible from the interface. It could be specified by the
programmer, or calculated by the compiler. I'm not sure which one
would fit the D philosophy best. It would work exactly like C++ const
member functions, so that you could call a non-readOnly member
function only on an not readOnly object. (Wanna extend the concept of
properties to apply to functions, too?)

Antti
Oct 16 2002
next sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Antti Sykari wrote:
 Current situation:
  - Integer values are passed by value
  - I assume that structs are passed by value as well, correct me 
    if I'm wrong
  - Objects are passed by reference
  - Array objects are passed by the infamous "something in between"
Well... everything is passed by cell. The cell of an instance is its reference, the cell of a struct is its fields. An array's cell is its length and data pointer. It's not in-between at all, just a struct (explicitly so - DLI and DMD both exploit this in implementing Phobos), and could only be said to be in-between if you insist on thinking of it as an array object when it's clearly not.
 Another problem is:
  - Objects cannot be passed with "read-only" semantics
  - The same holds for arrays, I think
 
 What we would ideally like is:
 - The visible effect of passing parameters should be the same
   regardless of the type of object
 - The user should be able to specify "constness" (C++-term) in
   function arguments
 
 Right?
I don't see how that makes me work faster or better, so no.
 One solution could be:
 
 Pass everything "const" by default. Then it doesn't matter whether the
 parameters are passed by reference, value, or whatever. The compiler
 may determine the most efficient calling convention. For example, it
 could just pass everything in registers and the function would not
 change their values. Or it could pass a pointer to stack or heap, if a
 bigger object is passed. But still, the function should only be able
 to call read-only ("const") functions of that class.
const has no effect on whether an argument can or should be put in a register. That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method. The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue. However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder). By which point you may as well have a ready slot waiting. If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was). Register arguments in virtual methods could be a win, but it could be an ugly loss as well, as the decision has to be made based on the arguments rather than the body. I haven't looked into fastcall in any detail at all.
Oct 16 2002
next sibling parent "Sandor Hojtsy" <hojtsy index.hu> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aojne9$2hs1$1 digitaldaemon.com...
 Antti Sykari wrote:
 Current situation:
  - Integer values are passed by value
  - I assume that structs are passed by value as well, correct me
    if I'm wrong
  - Objects are passed by reference
  - Array objects are passed by the infamous "something in between"
Well... everything is passed by cell. The cell of an instance is its reference, the cell of a struct is its fields. An array's cell is its length and data pointer. It's not in-between at all, just a struct (explicitly so - DLI and DMD both exploit this in implementing Phobos), and could only be said to be in-between if you insist on thinking of it as an array object when it's clearly not.
Yes. But I think we need a (possibly separate) array type with the properties of an object. Like self-encapsulation and inheritance.
 Another problem is:
  - Objects cannot be passed with "read-only" semantics
  - The same holds for arrays, I think

 What we would ideally like is:
 - The visible effect of passing parameters should be the same
   regardless of the type of object
 - The user should be able to specify "constness" (C++-term) in
   function arguments

 Right?
I don't see how that makes me work faster or better, so no.
Const helps in 1) Avoiding some bugs 2) Expressive power 3) Self documentation Sandor
Oct 16 2002
prev sibling next sibling parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aojne9$2hs1$1 digitaldaemon.com...
 Antti Sykari wrote:
 Another problem is:
  - Objects cannot be passed with "read-only" semantics
  - The same holds for arrays, I think

 What we would ideally like is:
 - The visible effect of passing parameters should be the same
   regardless of the type of object
 - The user should be able to specify "constness" (C++-term) in
   function arguments

 Right?
I don't see how that makes me work faster or better, so no.
That's essentially what the "in" keyword does on a parameter.
 One solution could be:

 Pass everything "const" by default. Then it doesn't matter whether the
 parameters are passed by reference, value, or whatever. The compiler
 may determine the most efficient calling convention. For example, it
 could just pass everything in registers and the function would not
 change their values. Or it could pass a pointer to stack or heap, if a
 bigger object is passed. But still, the function should only be able
 to call read-only ("const") functions of that class.
const has no effect on whether an argument can or should be put in a register. That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method. The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue. However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder). By which point you may as well have a ready slot waiting. If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).
The arguments should always go in registers when possible. The callee should just promise not to change any of them. The compiler should try to enforce it too. Why should you be able to write to your parameters? It should be an error unless they're declared inout or out. I'm not saying it has to be explicit. But any constraint here will only give the compiler leverage, something it doesn't have otherwise. It's a form of contract, a fairly primitive one at that. I agree about the puny X86 architecture. I am truly surprised it has weathered the time. Intel has never innovated much in this respect. Always short on registers.
 Register arguments in virtual methods could be a win, but it could be an
 ugly loss as well, as the decision has to be made based on the arguments
 rather than the body.  I haven't looked into fastcall in any detail at
all. Perhaps you should before spouting off about having to dump all registers to stack before every function call. The calling convention just has to have the called function guarantee not to change any of one set of registers, while the calling function expects to have all registers except for those potentially (likely) trashed. That's what fastcall is. And it passes the first four int parameters in such and such registers etc. Layout also. Using such a convention the caller can cache core values in the registers that aren't involved in parameter passing and that are guaranteed to be saved. The callee will save and restore them if it needs them. Isn't that better? Sean
Oct 17 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Sean L. Palmer wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:aojne9$2hs1$1 digitaldaemon.com...
 
Antti Sykari wrote:

Another problem is:
 - Objects cannot be passed with "read-only" semantics
 - The same holds for arrays, I think

What we would ideally like is:
- The visible effect of passing parameters should be the same
  regardless of the type of object
- The user should be able to specify "constness" (C++-term) in
  function arguments

Right?
I don't see how that makes me work faster or better, so no.
That's essentially what the "in" keyword does on a parameter.
One solution could be:

Pass everything "const" by default. Then it doesn't matter whether the
parameters are passed by reference, value, or whatever. The compiler
may determine the most efficient calling convention. For example, it
could just pass everything in registers and the function would not
change their values. Or it could pass a pointer to stack or heap, if a
bigger object is passed. But still, the function should only be able
to call read-only ("const") functions of that class.
const has no effect on whether an argument can or should be put in a register. That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method. The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue. However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder). By which point you may as well have a ready slot waiting. If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).
The arguments should always go in registers when possible. The callee should just promise not to change any of them. The compiler should try to enforce it too. Why should you be able to write to your parameters? It should be an error unless they're declared inout or out.
Why should you deny the capability? I don't have to argue in favour of variable in arguments; it's already there as it has been for thirty years. You have to show that it would be a better world if they were normally const.
 I'm not saying it has to be explicit.  But any constraint here will only
 give the compiler leverage, something it doesn't have otherwise.  It's a
 form of contract, a fairly primitive one at that.
The compiler can't use it as leverage for anything. Proving that a parameter isn't changed is a simple enough operation that is done for any optimisations. If it's used in a final method call or function, that can still be determined if the body is available. If a virtual method call is made or the body isn't available for any function that is called, it's dirtied regardless, even if const is applied, as it's impossible to say whether the object is modified in a non-const scope or if the const is casted off inside. Being able to cast off const just shows how fragile it is. I can't even get MSVC to report it as an error. It should be stressed that it's not closing any doors by allowing this - the first restriction closes all of them already.
Register arguments in virtual methods could be a win, but it could be an
ugly loss as well, as the decision has to be made based on the arguments
rather than the body.  I haven't looked into fastcall in any detail at
all. Perhaps you should before spouting off about having to dump all registers to stack before every function call. The calling convention just has to have the called function guarantee not to change any of one set of registers, while the calling function expects to have all registers except for those potentially (likely) trashed. That's what fastcall is. And it passes the first four int parameters in such and such registers etc. Layout also. Using such a convention the caller can cache core values in the registers that aren't involved in parameter passing and that are guaranteed to be saved. The callee will save and restore them if it needs them. Isn't that better?
I know what fastcall is; I was meaning in a comparison for virtual methods. How on Earth could I possibly attach passing on registers with fastcall - which isn't mentioned in the first message - without knowing what I was talking about? Who flushes the registers to the stack - caller or callee - is irrelevant if the operation must be done for almost all code, although it's slightly more expensive to do it in the callee. I could find only one comparison on fastcall versus cdecl and a test shows it doesn't apply to current MSVC. Using the function: int test (int x, int y, int z) { return x + y + z; } I've lost my clock count table but a from-memory comparison shows that this function takes 12 cycles in cdecl and 6 in fastcall. But when I do: int w (); int test (int x, int y, int z) { return x + y + z + w (); } The cdecl form is 14 clock cycles, while the fastcall form is 24. This IS partly evened out by the caller, but even that depends upon the vicissitudes of how it's being called. If any argument in is a function call or an expression, it'll be more expensive overall, and will quickly equal cdecl in the first test, too. A very temperamental optimisation applied with a sledge hammer. Functions which both have to be bodyless and could benefit from fastcall (not only in the body, but in their usage) are, thankfully, vanishingly small. Certainly not worth the effort to support them.
Oct 17 2002
parent "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
I'm not going to bend over backwards for you.  If you can't or won't see the
benefit, I'm not going to be able to convince you otherwise.  It appears
that others have tried and failed.

Just keep an open mind.

Isn't consistency worth anything?

A slight improvement to the status quo is not what I'm after.  I want a big
paradigm shift.  Quibbling about calling conventions is irrelevant to my
goals.  Some people were perfectly happy back in the C world.  I am not one
of them.

But I will say that any calling convention that forces preemptive,
speculative flushing of registers to stack is broken.  And any interface
specification that gives me no guarantees that a function won't modify my
parameter I'm sending in is also broken since to be safe I'd have to hand
the function a *copy* of my data.  That drawback should be obvious to you.
Why should I have to explain it to you?

Sean

"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aom887$24sa$1 digitaldaemon.com...
 Sean L. Palmer wrote:
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:aojne9$2hs1$1 digitaldaemon.com...

Antti Sykari wrote:

Another problem is:
 - Objects cannot be passed with "read-only" semantics
 - The same holds for arrays, I think

What we would ideally like is:
- The visible effect of passing parameters should be the same
  regardless of the type of object
- The user should be able to specify "constness" (C++-term) in
  function arguments

Right?
I don't see how that makes me work faster or better, so no.
That's essentially what the "in" keyword does on a parameter.
One solution could be:

Pass everything "const" by default. Then it doesn't matter whether the
parameters are passed by reference, value, or whatever. The compiler
may determine the most efficient calling convention. For example, it
could just pass everything in registers and the function would not
change their values. Or it could pass a pointer to stack or heap, if a
bigger object is passed. But still, the function should only be able
to call read-only ("const") functions of that class.
const has no effect on whether an argument can or should be put in a register. That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method. The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue. However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder). By which point you may as well have a ready slot waiting. If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).
The arguments should always go in registers when possible. The callee should just promise not to change any of them. The compiler should try
to
 enforce it too.  Why should you be able to write to your parameters?  It
 should be an error unless they're declared inout or out.
Why should you deny the capability? I don't have to argue in favour of variable in arguments; it's already there as it has been for thirty years. You have to show that it would be a better world if they were normally const.
 I'm not saying it has to be explicit.  But any constraint here will only
 give the compiler leverage, something it doesn't have otherwise.  It's a
 form of contract, a fairly primitive one at that.
The compiler can't use it as leverage for anything. Proving that a parameter isn't changed is a simple enough operation that is done for any optimisations. If it's used in a final method call or function, that can still be determined if the body is available. If a virtual method call is made or the body isn't available for any function that is called, it's dirtied regardless, even if const is applied, as it's impossible to say whether the object is modified in a non-const scope or if the const is casted off inside. Being able to cast off const just shows how fragile it is. I can't even get MSVC to report it as an error. It should be stressed that it's not closing any doors by allowing this - the first restriction closes all of them already.
Register arguments in virtual methods could be a win, but it could be an
ugly loss as well, as the decision has to be made based on the arguments
rather than the body.  I haven't looked into fastcall in any detail at
all. Perhaps you should before spouting off about having to dump all
registers to
 stack before every function call.  The calling convention just has to
have
 the called function guarantee not to change any of one set of registers,
 while the calling function expects to have all registers except for
those
 potentially (likely) trashed.  That's what fastcall is.  And it passes
the
 first four int parameters in such and such registers etc.  Layout also.
 Using such a convention the caller can cache core values in the
registers
 that aren't involved in parameter passing and that are guaranteed to be
 saved.  The callee will save and restore them if it needs them.  Isn't
that
 better?
I know what fastcall is; I was meaning in a comparison for virtual methods. How on Earth could I possibly attach passing on registers with fastcall - which isn't mentioned in the first message - without knowing what I was talking about? Who flushes the registers to the stack - caller or callee - is irrelevant if the operation must be done for almost all code, although it's slightly more expensive to do it in the callee. I could find only one comparison on fastcall versus cdecl and a test shows it doesn't apply to current MSVC. Using the function: int test (int x, int y, int z) { return x + y + z; } I've lost my clock count table but a from-memory comparison shows that this function takes 12 cycles in cdecl and 6 in fastcall. But when I do: int w (); int test (int x, int y, int z) { return x + y + z + w (); } The cdecl form is 14 clock cycles, while the fastcall form is 24. This IS partly evened out by the caller, but even that depends upon the vicissitudes of how it's being called. If any argument in is a function call or an expression, it'll be more expensive overall, and will quickly equal cdecl in the first test, too. A very temperamental optimisation applied with a sledge hammer. Functions which both have to be bodyless and could benefit from fastcall (not only in the body, but in their usage) are, thankfully, vanishingly small. Certainly not worth the effort to support them.
Oct 17 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:aojne9$2hs1$1 digitaldaemon.com...
 Register arguments in virtual methods could be a win, but it could be an
 ugly loss as well, as the decision has to be made based on the arguments
 rather than the body.  I haven't looked into fastcall in any detail at
all. That's just the problem with fastcall. It can make things slower in many cases. A better algorithm would be to determine the parameter convention after optimizing the body of the function, then if any parameters are enregistered, pass them in those registers. DMD doesn't do this, of course, but D is designed so the door is open for that kind of optimization, which could be a big win.
Oct 27 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
I agree:  Pass all parameters as readonly by default unless you declare
"out" or "inout" parameter.

I do kinda like your syntax.  Bang isn't used for anything in D except unary
not, but I am not sure it'd parse unambiguously in a parameter block.  If it
looks like a type it is a type.  So I guess it would work.  It certainly
does draw attention to the *modified* parameters, and cleans up the syntax
tremendously compared to the equivalent C++, even more than D's current
in/out/inout does.  You could use ? for inout probably.  I imagine : is
usable in a parameter list also if you want to add more stuff there.  May be
useful to separate "in" section from "out" section kinda like public:

Try this:

foo(in : int a, int b; out : float result)
{
    result = a + b;
}

Or even (add simple relational contracts!):

foo( in { int a >= 0, int b > 0 }; out { double result >= 1.0 } )
{
    result = a + b;
}

How would you extend the concept of properties to apply to functions?

Sean

"Antti Sykari" <antti.sykari housemarque.fi> wrote in message
news:87wuoig5h3.fsf cs190188.pp.htv.fi...
 One solution could be:

 Pass everything "const" by default. Then it doesn't matter whether the
 parameters are passed by reference, value, or whatever. The compiler
 may determine the most efficient calling convention. For example, it
 could just pass everything in registers and the function would not
 change their values. Or it could pass a pointer to stack or heap, if a
 bigger object is passed. But still, the function should only be able
 to call read-only ("const") functions of that class.

 int f(int x, complex y, int z[])
 {
         // x, y, z are passed in registers (for efficiency)

         // f may not change x, y, z or their contents, but can use
         // their values
 }

 int f(BigObject o)
 {
         // o is passed by reference (implemented as passing a pointer)

         // still f could not change its value, only call it's
         // "const" functions
 }

 If function should be able to change its arguments, it should declare
 it explicitly with a keyword. What keyword? I can't say. I use "inout"
 here which might not be the best solution.

 int f(BigObject x, BigObject y, inout BigObject z)
 {
         // may not change x or y, but can change z
 }

 Or maybe it could use a different syntax like:

 int f(BigObject x, BigObject y, BigObject! z)
 {
         // may not change x or y, but can change z
 }

 This would be D equivalent of the C++ function:

 int f(const BigObject& x, const BigObject& y, BigObject& z) { .. }

 but IMHO looks simpler and takes the attention to where it belongs: in
 the z variable, which is in the danger of being modified by the
 function.

 Naturally, if function _could_ change its arguments, they should be
 passed by reference, whether they are integral types, objects, arrays,
 or structs.

 The downside with this scheme is that D doesn't currently support the
 concept of "read-only object". Now, I don't like the "const" keyword
 in function declarations, since it already means something different
 (namely, the usage like "const int x_size = 640;"). However, now that
 we have properties, maybe objects could have a context-dependent
 property "readOnly":

 f(BigObject o)
 {
         assert(o.readOnly == true);
 }

 g(BigObject! o)
 {
         assert(o.readOnly == false);
 }

 Similarly, member functions should have this property as well, and it
 should be visible from the interface. It could be specified by the
 programmer, or calculated by the compiler. I'm not sure which one
 would fit the D philosophy best. It would work exactly like C++ const
 member functions, so that you could call a non-readOnly member
 function only on an not readOnly object. (Wanna extend the concept of
 properties to apply to functions, too?)
Go on... ;)
 Antti
Sean
Oct 17 2002
parent antti.sykari housemarque.fi writes:
"Sean L. Palmer" <seanpalmer directvinternet.com> writes:

 How would you extend the concept of properties to apply to functions?
http://www.digitalmars.com/d/property.html does not introduce very many of properties, but states: "Every type and expression has properties that can be queried." Uses of properties in general seem to fall into the following categories (I'll just categorize those of floating point numbers, since there are a lot of them): 1. A meta facility to get knowledge of a type or an expression: .size size in bytes .init initializer 2. A collection of constants closely related to the type: .max largest representable value that's not infinity .min smallest representable value that's not 0 .infinity infinity value .nan NaN value .mantissa number of bits in mantissa .maxExp maximum exponent as power of 2 (?) .digits number of digits of precision .epsilon smallest increment 3. A set of functions which can be used to get a property of a floating point or possibly integral number: .sign 1 if -, 0 if + .isnan 1 if nan, 0 if not .isinfinite 1 if +-infinity, 0 if not .isnormal 1 if not nan or infinity, 0 if Now I don't know if the number and nature of these properties are fixed, but functions being first-class citizens and all, at least with regard to passing them in parameters, why not throw in some properties for them also: class X { void foo(int x) { m_x = 5; } private: int m_x; } void g() { if (X.foo.isMemberFunction) { // foo is a member function! Now this can't possibly be // useful, except in template code. Who knows? } if (X.foo.synchronized) { // foo is a synchronized function (is there such a concept // in D?) } if (X.foo.final) { // foo cannot be overridden in a derived class } if (X.foo.public) { // foo is public } } But I just realized that this pretty much conflicts with the getter/setter issue in case that X.foo happens to be a variable. Other ideas for function properties: thread-safety; number of arguments; even types or names of arguments, and places and line numbers where the function is defined, as in __FILE__ && __LINE__; Many of these ideas could be extended to the properties of classes, and they would mostly fit into the category 1 of properties ("collecting meta-linguistic information about types and functions") I actually found most of the properties above in http://www.csci.csusb.edu/dick/samples/java.glossary.html, in the "Modifiers" section. How funny of that, the language is java. :) Another idea that popped into my mind was that the programmer could set the properties for arbitrary types such as one could mark "the class is thread-safe" or something like that by saying T.threadSafe = true; This was, of course, a stupid idea since it can be already done like: class T { // all member functions and accessed synchronized! const bool threadSafe = true; } Well, these are just ideas, not a long-thought proposition or anything. They are presented in the hope that somebody may find inspiration in them. Now, back to the "olden days, when EVIL ruled":
 I imagine : is usable in a parameter list also if you want to add
 more stuff there.  May be useful to separate "in" section from "out"
 section kinda like public:

 Try this:

 foo(in : int a, int b; out : float result)
 {
     result = a + b;
 }
A silly idea along the lines of good old pre-ANSI C: foo(a, b, result) in: int a, b; out: float result; { } It is, after all, a sort of natural style of indentation if you have to write a lot of stuff in the function declaration. If I had to include "in" or "out" in the function declaration, I'd probably indent it like: void foo( in int a, in int b, out float result) in { assert(a >= 0); assert(b >= 0); } out (result) { assert(result >= 1); } body { // ... } so as to enhance readability. (Consequent series of <visibility class> + <type> + <variable name> is bit more difficult to read than just <type> + <name>, hence the preference for vertical layout instead of horizontal.)
 Or even (add simple relational contracts!):

 foo( in { int a >= 0, int b > 0 }; out { double result >= 1.0 } )
 {
     result = a + b;
 }
The same in K&R style: foo(a, b, result) in: int a >= 0; // or just invariant { assert(a >= 0); } or something int b > 0; out: double result >= 1.0; { result = 1.0 + a + b; } [I took the liberty of changing the code because it broke the contract] It's more readable than if it was laid out on just one line, right? *grin* I am not serious about this, of course. :) Yet I have an afterthought: one of the reasons K&R style was abandoned must've been that it is tedious to maintain two longish interface specification for the function. (Although C isn't particularly picky on function declarations back then, it is considered good manners.) But D does not have header files, so abandoning K&R style function declarations doesn't have that excuse anymore. Antti.
Oct 17 2002