www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: 'auto' members and returning 'auto' instances

reply Daniel Keep <daniel.keep.lists gmail.com> writes:
From the D docs (attribute.html#auto):

"Auto cannot be applied to globals, statics, **data members**, inout or
out parameters. Arrays of autos are not allowed, and **auto function
return values** are not allowed. Assignment to an auto, other than
initialization, is not allowed.

Rationale: **These restrictions may get relaxed in the future if a
compelling reason to appears**."

Proposal
========

I'd like to propose the following additions to the abilities of the
'auto' attribute in D:

1. Allow 'auto' function return values, and
2. Allow 'auto' members.

I will try to outline the specifics of these additions, and why I think
they would be a valuable addition to D.  I ask that you treat the two
proposals as being separate, although related.

Also, to try and disambiguate what I mean by 'auto', I will use the
following terms:

* auto keyword: the "auto" keyword itself
* auto class: class with the auto keyword in its declaration
* auto variable: a variable that was declared with the auto keyword
* auto instance: an instance of an auto class, or an object instance
that was assigned to an auto variable

Details
=======

Allow 'auto' function return values
-----------------------------------

In the current edition of the D language, you may not return an auto
instance from a function.  I assume that the rationale behind this
decision is simply that: if you can return an auto instance, then you
cannot guarantee that the compiler can safely destroy all auto variables
at the end of the scope.

I assert that with four simple rules, this problem disappears, allowing
you to have auto instance return values.

The rules are:

(1.) You may return an auto instance if it has been created using the
'new' keyword, and not yet assigned to any auto variable.  eg:

# return new AutoClass; // This is permitted.

# auto AutoClass a = new AutoClass;
# return a; // This is not permitted.

(2.) You may return an auto instance if it is the result of a function
that returns an auto instance.  eg:

# auto AutoClass someFunction()
# {
#     return new AutoClass;
# }
#
# return someFunction(); // This is permitted.

(3.) If a function returns an auto instance, this instance must either
be stored in an auto variable, returned from the current function, or
immediately destroyed.  eg:

# someFunction();

Implicitly becomes:

# {
#     auto AutoClass x = someFunction();
# }

(Alternate 3.) If a function returns an auto instance, this instance
must be explicitly stored in an auto variable so that it can be
destroyed at scope exit.  eg:

# someFunction(); // This is not permitted.
# auto AutoClass x = someFunction(); // It must be written like this

NB: I prefer the first version of Rule #3, but this one might be easier
to implement?

(4.) All other forms involving auto instances and return statements are
disallowed.  eg:

# auto AutoClass a = new AutoClass;
# return a; // This is not allowed before or after proposal



Rule #1 preserves the compiler's ability to destroy auto variables at
scope exit because the newly created object was never assigned to an
auto variable: thus it won't be destroyed.

Rule #2 also preserves the ability to destroy auto variables for the
same reason.

Rule #3 ensures that objects returned from functions are always destroyed.



A valid question that comes up is: wouldn't it be possible to violate
this by passing an auto instance into a function and then returning it?

# auto AutoClass foo(AutoClass bar)
# {
#     return bar;
# }

In this case, no it wouldn't.  Remember that AutoClass is, well, an auto
class, and thus all instances of it are also auto.  Not only that, but
the return statement does not satisfy either rules 1 or 2.  If you
wanted to do something like this, you would need to use this idiom:

# auto AutoClass foo(AutoClass bar)
# {
#     return bar.dup;
# }

Where dup is a function that creates a new, independent instance,
allowing the original to be safely destroyed.  You could also do this
using *explicit* copy constructors, like so:

# auto AutoClass foo(AutoClass bar)
# {
#     return new AutoClass(bar);
# }
#
# auto class AutoClass
# {
#     // ...
#     this(AutoClass obj)
#     {
#         // copy state of obj to this new instance
#     }
#     // ...
#     auto AutoClass dup()
#     {
#         return new AutoClass(this);
#     }
# }

Allow 'auto' members
--------------------

From the definition of the auto keyword, it is not immediately apparent
what applying the auto keyword to a member would mean.

I propose that we define it to mean this:

"An auto member is a member of an auto class that is declared with the
auto keyword.  An auto member may ONLY be assigned to from inside the
class' constructor, and may only be assigned to once.  When the owning
object's instance is destroyed, all auto members are also automatically
destroyed.  It is an error to declare an auto member in a non-auto class."

In other words, it would behave much like a 'const' member.  It is worth
noting that the previous proposal regarding return values does not allow
these auto instances to 'escape' from the object: you still can't
directly return the contents of an auto member: you need to dup it first.

An alternate version is:

"An auto member is a member of an auto class that is declared with the
auto keyword.  **When a non-null auto member is assigned to, the
existing auto object is destroyed.**  When the owning object's instance
is destroyed, all auto members are also automatically destroyed.  It is
an error to declare an auto member in a non-auto class."

In both cases, auto members would require the same assignment semantics
as were used for return statements in the first proposal: that is, you
may only assign to auto members if you can prove that the auto instance
is not pointed to be anything else.

I am not entirely sure which of the above would be most useful; the
first is perhaps more consistent with the current auto keyword's semantics.

Rationale
=========

The cairo drawing API makes use of pointers to structs as "objects".
These objects are reference counted.  This is done not only to try to
prevent memory leaks, but also because graphics objects can sometimes be
very limited, or consume large amounts of memory: being able to
deterministically destroy them is a good thing.

When I originally started writing the object-oriented binding for cairo,
I had envisaged using auto classes to implement a kind of reference
counting system in D.  That is, all variables would be auto, and you
could get a "reference" to a cairo object by dup'ing the D object
wrapping it.

Of course, this fell through when I discovered that you can't return
auto instances.

This is not the only place where they would have been useful: there are
many cases in Cairo where overloading the constructor is either not
feasible, or would result in a massive number of "dummy" classes whose
sole purpose is to provide a new constructor.  In their place, static
factory methods are often used; but since these can't return auto
instances, you can't use auto classes at all.

Also, as I hinted at above, this proposal would bring one of the few
memory management techniques that D does not yet support into the fold:
reference counting.  As far as I know, there isn't another language in
existence that supports explicit memory management, lazy management with
a GC, RAII, scoped destruction AND reference counting.

The proposal regarding auto members is mostly an offshoot of my thinking
process: by adding them, we take auto instances from being solely one
level-deep, to being arbitrarily deep trees of objects that will be
destroyed the instant they are no longer needed, thus making them MUCH
more useful.

Summary
=======

I realise it may be a bit late in the game to get this implemented for D
1.0, but I would be happy to simply have it flagged as being "in the
next version".  It's better to know it's coming eventually, then not to
have it at all.

Also, if I've made any silly mistakes in the above, please feel free to
correct me :)

	-- Daniel Keep

-- 

v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D
a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP    http://hackerkey.com/
May 22 2006
parent reply Rmy Mouza <Rmy_member pathlink.com> writes:
This seems to be like a kind of C++ "const" for memory  la D. Would these auto
variables complicate the type system, as in C++ ? Could it be done using some
"scope ( exit ) { delete var1, var2, var... ; }" ?
This extended use of auto may add some ambiguities with the "auto" type
inference.  This latter may use an other keyword like "let" to have things like:
let sc = new SomeClass (); // type inference, with an Ocaml look. 
auto let sc = new SomeAutoClass (); // auto variable and type inference.
May 23 2006
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Hooray!  Someone replied!  ^_^

R������������������������������
������ wrote:
 This seems to be like a kind of C++ "const" for memory � la D. Would these
auto
 variables complicate the type system, as in C++ ? Could it be done using some
 "scope ( exit ) { delete var1, var2, var... ; }" ?
 This extended use of auto may add some ambiguities with the "auto" type
 inference.  This latter may use an other keyword like "let" to have things
like:
 let sc = new SomeClass (); // type inference, with an Ocaml look. 
 auto let sc = new SomeAutoClass (); // auto variable and type inference.
 

It doesn't change existing semantics for auto-destroyed objects EXCEPT to allow them to be a return type, and allow them as object members. Type inference will continue to work exactly as it did before. As for doing it manually, indeed you could! But then, 'auto' in it's current form can be done manually. 'auto' exists to help the programmer write safer, more predictable code. This is just extending where you can use it a little. -- Daniel Keep P.S. Thanks for commenting; I was beginning to wonder if anyone had even read it :P -- v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 23 2006