comp.lang.ada
 help / color / mirror / Atom feed
From: Simon Belmont <sbelmont700@gmail.com>
Subject: Re: Unknown discriminants with nested records
Date: Thu, 16 Jun 2011 18:48:51 -0700 (PDT)
Date: 2011-06-16T18:48:51-07:00	[thread overview]
Message-ID: <2191e8b5-e827-41e8-b8a0-f02b8a68362c@v10g2000yqn.googlegroups.com> (raw)
In-Reply-To: e3d8de4a-b442-4494-abef-4fe6c3eb7094@r27g2000prr.googlegroups.com

On Jun 16, 8:39 pm, Adam Beneschan <a...@irvine.com> wrote:
> On Jun 16, 4:53 pm, Simon Belmont <sbelmont...@gmail.com> wrote:
>
>
>
>
>
> > New to ada (2005), and trying to figure out how to create records of
> > other records with unknown discriminants, e.g. like this:
>
> > package Test_Package is
>
> >     type Foo_Type (<>) is limited private;  -- some record with
> > unknown discriminants.
>
> >     t1: Foo_Type := MakeAFooType; -- works fine being built by a
> > function
>
> >     type BarType is
> >     record
> >      B1: Integer;
> >      -- other record elements
> >      B2: FooType := MakeAFooType;  -- Error, unconstrained subtype.
> >     end record;
>
> > private
>
> >     type FooType is limited
> >     record
> >      F1: Integer;
> >     end record;
>
> > end Test_Package;
>
> > The (<>) seems to be the defacto way to limit creation to a function,
> > yet it seemingly precludes you from using it in a nested record.  It
> > seems strange that there is the ability to declare a standalone object
> > of that type, but trying to nest one within another record throws an
> > "unconstrained subtype" error; if it knows the constraints one place,
> > why doesn't it know them several lines down?
>
> To start with, it's helpful to post the actual source you tried.
> Clearly this isn't, since you didn't spell Foo_Type consistently, but
> the more important problem is that you've stuck the declarations T1
> and BarType in the middle of Test_Package where they don't belong.
> You can't declare T1 to be a Foo_Type at that point, because
> Foo_Type's definition hasn't been finished.  Anyway, I'll assume that
> T1 and BarType are actually declared in some other package
> Test_Package_2 that with's and uses Test_Package.
>
> The basic problem is this: When Test_Package_2 is elaborated, and it
> elaborates the definition of T1, it will call MakeAFooType at that
> point.  The discriminants (if any) of the value will then be
> determined, so T1's discriminants will then be known.
>
> But when BarType is elaborated, MakeAFooType isn't called.  The use of
> MakeAFooType here is a *default* expression.  When you later declare
>
>    B : BarType;
>
> with no initial expression, MakeAFooType will be called at *that*
> point, when B is elaborated.  It's also possible to declare B with an
> initial expression like
>
>    B : BarType := (B1 => 1, B2 => Make_A_Different_Kind_Of_Foo);
>
> so that MakeAFooType wouldn't be called at all.  So here, the
> constraints of the B2 component won't be what MakeAFooType would
> return.
>
> Hopefully that will answer your question about "why doesn't it know
> [the constraints] several lines down".  It doesn't know because that
> syntax doesn't actually result in a function call.
>
> The problem here is that since the initial expression on the component
> is just a *default* and may not be the actual initial expression for a
> B2 component, the compiler thus doesn't know what constraints a B2
> will have.  In theory, since it's possible for objects with different
> constraints to have different sizes, the compiler thus may not know
> how big a BarType will be, and I think that's the technical reason why
> this sort of component type is prohibited.  (In practice, it might be
> possible to make this work, since often the size of a FooType won't
> vary; but it might be hard to do without introducing some incompatible
> changes in the language.  I don't know if any possible language
> changes have ever been discussed.)
>
> As for how to get around this: if it were me, I'd just lose the (<>).
> The (<>) was intended to define types with unknown discriminants;
> using it to force initialization functions to be used, when you know
> there won't really be any discriminants, is using this feature for a
> purpose for which it wasn't intended, I think.  Someone clever found
> that it works for this purpose, but obviously it doesn't work all the
> time.  Maybe a language feature should be added that allows the user
> to define a *definite* type that still isn't allowed to be
> uninitialized.  Here's a possible way to force all uses of this type
> to be initialized:
>
> package Test_Package is
>     type Foo_Type is limited private;
>     ... etc. etc.
> private
>     function Blow_Up return Integer;
>     type Foo_Type is limited
>        record
>           F1 : Integer := Blow_Up;
>        end record;
> end Test_Package;
>
> where Blow_Up is defined like:
>
>     function Blow_Up return Integer is
>     begin
>        raise Program_Error with "You forgot to initialize your
> Foo_Type";
>        return 0;  -- never executed
>     end Blow_Up;
>
> or something along those lines.  This won't be caught at compile time
> (although some compilers may display a warning that Program_Error will
> be raised), but a program that declares a variable that is or contains
> an uninitialized Foo_Type will bomb.
>
> > 4. Can anyone point me to the section in the LRM that specifies this
> > either way?
>
> 3.6(10).  This is in the section on array types, but the restriction
> on a component's type applies to record type components too.
>
>                                  -- Adam- Hide quoted text -
>
> - Show quoted text -

Thank you very much, that was the exact answer I was looking for.  I
was supposing too much about the operation of the unknown discriminent
marker; I figured it was more of a "known-but-I-won't-tell-you-because-
you-don't-need-to-know" marker than an actual "unknown", and that the
compiler would be able to determine that the record in question
didn't, in fact, have any real discriminents.

It would certainly be nice if there were some sort of language feature
for this, especially considering that, like you said, the entire
community makes it standard practice to deliberity misuse an
operator.  But if it's been 16 years without one, I doubt there it
would be high on the priority list (though IMHO these priorities seem
skewed; some of the things on the 2012 list seem frivilous compared to
more basic things like making sure objects are properly initialized).

As for the suggestion, I am loathe to circumvent the compile-time
safety (after all, that seems like the whole point of using Ada).  My
next thought is to simply make it an access type in the record, and
then create a new Foo on the heap each time someone uses the
hypothetical MakeABar, though I'm not sure using the heap that much is
any better...

In either case, thank you again for your spot-on explanation.



  reply	other threads:[~2011-06-17  1:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-16 23:53 Unknown discriminants with nested records Simon Belmont
2011-06-17  0:39 ` Adam Beneschan
2011-06-17  1:48   ` Simon Belmont [this message]
2011-06-17 14:50     ` Robert A Duff
2011-06-18  8:33       ` Yannick Duchêne (Hibou57)
2011-06-20 16:16         ` Adam Beneschan
2011-06-17  6:03   ` Jeffrey Carter
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox