Version 1.1 of acs/ac-00142.txt

Unformatted version of acs/ac-00142.txt version 1.1
Other versions for file acs/ac-00142.txt

!standard 7.5(2,1/2)          07-05-04 AC95-00142/01
!class confirmation 07-05-04
!status received no action 07-05-04
!status received 07-04-30
!subject Limited constants considered limiting
!summary
!appendix

From: Randy Brukardt
Date: Monday, April 30, 2007  6:25 PM

I've been thinking about the use of limited types, and in particular the
construction of packages using limited types. I've noticed a ramification of
the rules that seems somewhat unintuitive, although I'm unconvinced its
wrong. I thought I'd toss it out here and let the rest of you comment on it.

For non-limited types, the advice has always been to declare constants with
values that are commonly used or have special meanings, rather than
spreading them all over the place. That has generally proved to be good
advice.

That was difficult to do in Ada 95. To do that for limited types, the only
option was to declare a function; that had to return an existing object, so
that had to be declared in the body. And that object could not be constant
nor directly initialized, so it had to be initialized component-by-component
in the package body's elaboration part. That's four separate places. Yikes!

The Amendment eliminates the initialization issues and the need to have an
existing object. It also appears to allow useful constants -- but those
constants are much more limited than the function that they would replace.
That's because a constant is not one of the things allowed in limited
initialization expressions, while a function call is. That means that such a
constant cannot replace a function (or aggregate) in initializing a larger
object.

For instance, taking an example from Claw:

    package Claw.Registry is
         type Key_Type is new Limited_Controlled with private;

         HKey_Current_User : constant Key_Type;
         function HKey_Local_Machine return Key_Type;
    ...
    end Claw.Registry;

    type Application_Key_Type is record
        Key : Key_Type;
        Company : Unbounded_String;
        Application : Unbounded_String;
        Version : Unbounded_String;
    end record;

    Janus_Ada : constant Application_Key_Type :=
        (Key => HKey_Current_User, -- Illegal!
         Company => "RRS",
         Application => "Janus-Ada",
         Version => "");

    CBuild : constant Application_Key_Type :=
        (Key => HKey_Local_Machine, -- OK.
         Company => "RRS",
         Application => "CBuild",
         Version => "");

The issue, of course, is that a limited object's value usually includes it's
object identity. Since a constant is an object, it's value includes that
identity, and thus it can't be composed into a surrounding composite object.

From this, I have to conclude that designers need to avoid using limited
constants unless they *only* want the constant to be used as a marker to
operations. Otherwise, they should use a less limiting function (since it is
usually the best practice to not constrain your clients unnecessarily).

I found this fact rather counter-intuitive, although that may be mainly
because we've never had limited constants to reason about in the past. In
any case, it seems like something that book authors ought to be mentioning.

****************************************************************

From: Tucker Taft
Date: Monday, April 30, 2007  7:59 PM

The most fundamental property of a limited type is that
it can't meaningfully be copied.  If it could then one
could make the type non-limited, and make the type Controlled
and provide an "Adjust" routine if appropriate to do whatever
fixups are required upon copying.

The example you give below seems "unintuitive" to me only
because the type you make limited, Key_Type, doesn't seem
to fit the definition of a limited type, since it seems
like it *is* meaningful to copy it.

Can you explain the reason why you want it to be limited?

****************************************************************

From: Randy Brukardt
Date: Monday, April 30, 2007  8:21 PM

A Key is very similar to a File (in that you open it, close it, read from
it, etc.), and we followed a similar design to file handling for that
reason. It also simplified the implementation (which otherwise would need
messy reference counts or worse in order to determine when to free keys).

But the use of the type seems irrelevant to me. The issue exists for any
limited type that you might want to use as a component of a larger data
structure. Or are you claiming that such types don't exist?

If you replace "Key_Type" by "Ada.Text_IO.File_Type" in my example, you get
a similar result. You wouldn't want Standard_Input to be a constant, because
you then couldn't initialize a larger data structure with it (rather than
opening a file with the component as a parameter). And there wouldn't be any
other (language-defined) way to set such a component to Standard_Input
(there is no language-defined way to open it).

Maybe you find the ability to initialize a component in this way as odd, but
it seems natural enough to me. I suppose you could claim that File_Type
shouldn't be limited, but that train left a very long time ago...

****************************************************************

From: Matthew Heaney
Date: Monday, April 30, 2007  8:56 PM

> Maybe you find the ability to initialize a component in this way as odd, but
> it seems natural enough to me.

Couldn't you pass the key constant to a copy constructor, e.g.

  function New_Key (Key : Key_Type) return Key_Type;

That way you still wouldn't need to make the key type non-limited.

****************************************************************

From: Randy Brukardt
Date: Monday, April 30, 2007  9:13 PM

Sure, but it still requires a function (a copy constructor) that you
wouldn't necessarily have. And then you might as well forget the constant
altogether in favor of a constructor function, because that does both jobs
(providing a name for direct use, and providing a way for
initialize-in-place).

****************************************************************

From: Tucker Taft
Date: Monday, April 30, 2007  9:07 PM

I'm afraid I still don't quite get what it means to
"open" a Key.  But perhaps you are saying that it is
fine to copy a Key when it is *not* open, as it just
represents a "name."  Once it is open, it has more state
which would be awkward to allow copying, but it is OK
to copy when it is closed.  Perhaps the same could be
said of a File_Type, though they don't contain much
information of interest when closed.

In any case, the model seems to be that either you allow
copying or you don't in Ada for a given type, and that
isn't state dependent.  If you wanted to make it state
dependent, you would have to make it non-limited Controlled
and have Adjust fail or somehow reset the state if
the current state is incompatible with copying.

Another approach is to use two different types, a non-limited
one which holds the copyable information content, and a
limited one that need only exist while the object is
in its non-copyable state.

I don't see any solution for your concern without major
revamping of the limited type model.

****************************************************************

From: Randy Brukardt
Date: Monday, April 30, 2007 10:11 PM

> I'm afraid I still don't quite get what it means to
> "open" a Key.

It associates a name with the key, and opens the associated resource. There
are also some predefined keys that are always open (they form the root of
the registry). It works exactly like a typical file system; thus the mapping
is so apt.

> But perhaps you are saying that it is
> fine to copy a Key when it is *not* open, as it just
> represents a "name."  Once it is open, it has more state
> which would be awkward to allow copying, but it is OK
> to copy when it is closed.  Perhaps the same could be
> said of a File_Type, though they don't contain much
> information of interest when closed.

I'm not talking about copying anything in the limited type! I don't know
where you get that idea from; obviously I'm not communicating something.

The constants/functions represent the predefined keys, which you don't have
to open. In that sense, they work exactly like Standard_Input and
Standard_Output. You can have as many of them as you need, just as with null
(and Standard_Input and Standard_Output); all you are doing is setting the
mapping of the limited object to the predefined handle (as opposed to
opening a new one).

All I'm interested in is naming those predefined keys, preferably to provide
maximum functionality. I found it odd that a function would allow creating
new instances of the predefined keys, while it is not possible to do that
with a constant, even if both simply use the same aggregate. I've always
thought of  constants and functions as interchangable in the absense of
parameters and/or code; apparently that is not true.

> In any case, the model seems to be that either you allow
> copying or you don't in Ada for a given type, and that
> isn't state dependent.  If you wanted to make it state
> dependent, you would have to make it non-limited Controlled
> and have Adjust fail or somehow reset the state if
> the current state is incompatible with copying.

Of course, but I'm not copying anything in this example! (Certainly not at
the client level; what happens at the implementation level is irrelevant.)
I'm creating objects, and it's weird that you can't use constants to create
objects, but an absoletely equivalent function can be used. These things are
essentially equivalent in my view: constants are more of a name for a value
than an object that can mutate. So I'm now beginning to think that allowing
limited constants was a mistake, because they have none of the expected
behavior of a constant (especially given that they can mutate). Too late...

...
> I don't see any solution for your concern without major
> revamping of the limited type model.

We do that frequently, so that doesn't bother me. But I fail to see what
"copying" you are talking about, so I think you're solving the wrong
concern. I realize that there may be some case where the object identity is
so strong that you would not want any predefined constructors, but you'd
still want predefined objects, and the current scenario would work for that.
But I've never run into such a case. Much more typically, the underlying
implementation could support copying if you really wanted it (that's even
true for most implementations of tasks, and surely for protected objects
without entries -- the latter really shouldn't have been limited in the
first place).

The fact remains that if you have something predefined that could be
employed as an initial state, then it has to be defined as a function. If,
OTOH, you have something that could be employed as an object of its own, it
could be defined as a function or as a constant. That means that the only
time that you would ever want to use a constant is if that predefined value
cannot be used as an initial state. Package designers need to understand
that.

In any case, I don't think any language redesign is warranted. (Just because
I've never seen a need for a limited constant that wasn't an also an
initializer, doesn't mean that it doesn't exist.) But I do think that more
education is necessary: limited types are very unlike non-limited types, and
nothing that we've done in the Amendment has changed that in the least.

****************************************************************

From: Tucker Taft
Date: Tuesday, May  1, 2007  8:01 AM

 > ...
 > I'm not talking about copying anything in the limited type! I don't know
 > where you get that idea from; obviously I'm not communicating something.

That is probably true.  From what I see, you
are clearly trying to create a new object at least
part of which is a *copy* of an existing
limited constant object.

A limited constant object is first and foremost
an object.  If you define its value with an aggregate
I can see how you might think of it as a kind of
"macro" for the aggregate.  However, it might contain
a task or a protected component or a "<>" initialized
component with a non-trivial "Initialize" procedure, so
it is not trivial to know how to create yet another object
using the constant as a template.  Furthermore,
even though it is declared a constant, its initialization
might allocate non-constant heap objects that
are pointed-to from the constant.  Copying such a
constant object might quite easily create undesirable
sharing.

In this particular case, there is nothing inside your limited
constant object that has any difficult-to-copy properties,
but I don't see how the language could know that easily.

An aggregate is different from a constant, in that it is
an executable construct that builds a new object.  A
constant already is an object, and trying to use it as
a template for how to build another one isn't well defined
when the type is limited, since there is no well-defined
"copy" operation.

****************************************************************

From: Pascal Leroy
Date: Wednesday, May  2, 2007  4:07 AM

> I've always thought of  constants and functions as interchangable
> in the absense of parameters and/or code; apparently that is not true.

That has never been true, Randy.  Among other things, functions are
overloadable and they are inherited.  This often makes them preferable to
deferred constants.

And of course, prior to Ada 2005, you didn't have limited constants but
you had functions returning limited types (I realize that the
return-by-reference mechanism was not ideal, but as you pointed out it was
good enough for the predefined files in Text_IO and friends).

****************************************************************


Questions? Ask the ACAA Technical Agent