* Re: Overloading operator "=" for anonymous access types?
2019-01-14 23:08 ` Overloading operator "=" " Randy Brukardt
@ 2019-01-15 0:34 ` Shark8
2019-01-15 8:38 ` Dmitry A. Kazakov
` (2 subsequent siblings)
3 siblings, 0 replies; 17+ messages in thread
From: Shark8 @ 2019-01-15 0:34 UTC (permalink / raw)
On Monday, January 14, 2019 at 4:08:36 PM UTC-7, Randy Brukardt wrote:
>
> As always, best avoid anonymous access types unless you have to use one of
> their special features (dynamic accessibility, dispatching, special
> discriminant accessibility, or closures [for access-to-subprograms]). And
> better still, lets lobby to get those special features optionally available
> for named access types so no one every has to use an anonymous anything. :-)
Well, I'm all for getting rid of anonymous access types altogether -- though that might not be acceptable to the rest of the ARG as it would make previously-valid Ada non-valid, I think reducing the complexity of the language (and reduce instances of "a rule that barely makes sense anyway").
I thought there was an AI for first class subprograms / subprogram types, but I couldn't find it with a quick search... so either I'm misremembering or I'm just hitting all the wrong keywords in the search.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-14 23:08 ` Overloading operator "=" " Randy Brukardt
2019-01-15 0:34 ` Shark8
@ 2019-01-15 8:38 ` Dmitry A. Kazakov
2019-01-15 21:00 ` Randy Brukardt
2019-01-15 8:51 ` daicrkk
2019-01-17 3:20 ` Jere
3 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2019-01-15 8:38 UTC (permalink / raw)
On 2019-01-15 00:08, Randy Brukardt wrote:
> <daicrkk@googlemail.com> wrote in message
> news:0c56d9f4-8861-4c74-b170-a973e3789b08@googlegroups.com...
>
>> I second that. Access Cell is an access-to-object type whose designated
>> type is Cell (check), Cell has a user-defined primitive equality operator
>> (check) such that its result type is Boolean (check), it is declared
>> immediately within the same declaration list as Cell (check), at least one
>> of its operands is an access parameter with designated type Cell (both
>> operands are, check).
>> According to 4.5.2, universal_access "=" should never be allowed to kick in
>> at all here, not even with "L = null". Or am I missing something?
>
> Yup, I agree with this. My first thought when reading that example is that
> it is wrong, because I don't remember anywhere in Ada where the same
> operator with arguments of the same type means different things. I don't
> think the use of "null" could change that.
But the types are not same. It is universal_access vs. access.
> Dunno if John wrote that for a different version of Ada, or he was just
> confused by a rule that barely makes sense anyway.
>
> As always, best avoid anonymous access types unless you have to use one of
> their special features (dynamic accessibility, dispatching, special
> discriminant accessibility, or closures [for access-to-subprograms]). And
> better still, lets lobby to get those special features optionally available
> for named access types so no one every has to use an anonymous anything. :-)
Named or anonymous it makes little difference, IMO.
Here is a classic multi-method case. "=" is such an operation. null is
universal_access (4.2). For any access type P there are 3 equality
operations "=":
function "=" (Left, Right : universal_access) return Boolean;
type P is access T;
function "=" (Left : P; Right : universal_access) return Boolean;
function "=" (Left : universal_access; Right : P) return Boolean;
function "=" (Left, Right : P) return Boolean;
When the last one is overridden, what happens with the second and the third?
One of three possibilities:
1. It inherits the base operation:
function "=" (Left : P; Right : universal_access) return Boolean is
begin
return universal_access (Left) = Right;
end "=";
2. It silently overrides:
function "=" (Left : P; Right : universal_access) return Boolean is
begin
return Left = P (Right);
end "=";
3. It gets overridden abstract and comparison to null becomes illegal
because the operation is not defined.
[ The reference manual is shy to say anything about it. It claims that
universal_access is kind of class-wide, which would mean, if taken
seriously, that "=" overloads and must clash with the original "=".
Since it does not, universal_access is more like a parent type than
class-wide.]
It seems that in the OP's case as in the case with named access types #2
is in effect, which is illogical, inconsistent, unsafe, but would be
expected by most people.
Barnes' code presumes rather #1, which is logical, but confusing and
error-prone.
#3 would be consistent and safe:
if Ptr_Value = Ptr_Type (null) then -- Type conversion required
But it would not work with anonymous access types. So, if #3 were
adopted, then overriding for anonymous types must be banished.
All this is fine and good, except that overriding
function "=" (Left, Right : access T) return Boolean;
is also a primitive of T! You cannot banish it.
P.S. And, wouldn't it be better to fix the type system, no?
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-15 8:38 ` Dmitry A. Kazakov
@ 2019-01-15 21:00 ` Randy Brukardt
2019-01-16 15:42 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2019-01-15 21:00 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:q1k65j$1qqm$1@gioia.aioe.org...
...
> [ The reference manual is shy to say anything about it. It claims that
> universal_access is kind of class-wide, which would mean, if taken
> seriously, that "=" overloads and must clash with the original "=".
This is what happens. However, such a clash would mean that you could never
write a user-defined "=" for an anonymous access type. That would have been
a good idea, but it would have to have been enforced with a Legality Rule to
be sensible. Some thought that bad because of compatibility, so...
> Since it does not, universal_access is more like a parent type than
> class-wide.]
...there is a hack to have a preference for the user-defined one. That
doesn't change the the fact that universal_access is class-wide, it just
make it possible to write a user-defined operator.
>P.S. And, wouldn't it be better to fix the type system, no?
This wart would be one of the things that would make "fixing the type
system" so much harder. A proper solution (and the one we should have used
in the first place) is to declare a "=" for every access type. I think we
wanted to avoid that as anonymous access can be declared in places where
declarations aren't allowed, so we came up with something worse. :-)
It's the idea of anonymous access types that destroys the type system that
you have in mind. Your system keeps the types and operations together, and
that makes no sense for an anonymous type (what are the operations for an
anonymous type, and where are they declared? Any answer is either magical or
nonsense.)
One has to get rid of nonsense things before one could regularize the type
system, especially upon the lines you have been suggesting for years. It's
not really possible for Ada; you would end up with an Ada-like language.
Randy.
This is just another Ada
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-15 21:00 ` Randy Brukardt
@ 2019-01-16 15:42 ` Dmitry A. Kazakov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2019-01-16 15:42 UTC (permalink / raw)
On 2019-01-15 22:00, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:q1k65j$1qqm$1@gioia.aioe.org...
> ...
>> [ The reference manual is shy to say anything about it. It claims that
>> universal_access is kind of class-wide, which would mean, if taken
>> seriously, that "=" overloads and must clash with the original "=".
>
> This is what happens. However, such a clash would mean that you could never
> write a user-defined "=" for an anonymous access type. That would have been
> a good idea, but it would have to have been enforced with a Legality Rule to
> be sensible. Some thought that bad because of compatibility, so...
>
>> Since it does not, universal_access is more like a parent type than
>> class-wide.]
>
> ...there is a hack to have a preference for the user-defined one. That
> doesn't change the the fact that universal_access is class-wide, it just
> make it possible to write a user-defined operator.
Yes, you need hacks to handle inconsistencies and weaknesses.
>> P.S. And, wouldn't it be better to fix the type system, no?
>
> This wart would be one of the things that would make "fixing the type
> system" so much harder. A proper solution (and the one we should have used
> in the first place) is to declare a "=" for every access type. I think we
> wanted to avoid that as anonymous access can be declared in places where
> declarations aren't allowed, so we came up with something worse. :-)
I think the problem is that all operations with access T as an argument
should be primitive operations of T. That was the idea behind anonymous
access types all the time. But some types are not so much types to have
classes.
> It's the idea of anonymous access types that destroys the type system that
> you have in mind. Your system keeps the types and operations together, and
> that makes no sense for an anonymous type (what are the operations for an
> anonymous type, and where are they declared? Any answer is either magical or
> nonsense.)
Yes, there is a conflict between anonymous access type treated as a
standalone type and as a parameter passing mode of the target type.
> One has to get rid of nonsense things before one could regularize the type
> system, especially upon the lines you have been suggesting for years. It's
> not really possible for Ada; you would end up with an Ada-like language.
We may be able to express this in the terms of a more general type
system. If anonymous access would be a mere user-defined type with all
its perks that would be OK.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-14 23:08 ` Overloading operator "=" " Randy Brukardt
2019-01-15 0:34 ` Shark8
2019-01-15 8:38 ` Dmitry A. Kazakov
@ 2019-01-15 8:51 ` daicrkk
2019-01-15 11:15 ` Simon Wright
2019-01-17 3:20 ` Jere
3 siblings, 1 reply; 17+ messages in thread
From: daicrkk @ 2019-01-15 8:51 UTC (permalink / raw)
Thx for the insight on anonymous access types being somewhat problematic. I have not yet gotten so far into learning the language as to see this myself.
For anyone interested I have tried to summarize my findings with regard to the example (as I did on SO). Please correct me if I'm wrong:
According to the standard, L = null, R = null, L = R as well as L.Next = R.Next should each unambiguously call the user-defined operator =. universal_access operator = must not kick in at all here.
Reason:
The operands L, R, L.Next and R.Next violate the precondition in ARM 4.5.2(9.1-9.4) for interpreting = in these expressions as to mean operator = of the unviversal_access type:
All of these operands are of an access-to-object type (access Cell, check) whose designated type is Cell (check), Cell has a user-defined primitive equality operator (check) such that
its result type is Boolean (check);
it is declared immediately within the same declaration list as Cell (check); and
at least one of its operands is an access parameter with designated type Cell (both operands are, check).
The preference rule for operator = of the universal_access type in ARM 8.6(29.1) does not apply here, since it requires "two acceptable interpretations". But because of 4.5.2, operator = of the universal_access type is not an acceptable interpretation.
So there is no choice: in all cases (even L = null) it has to be the user-defined operator =.
The example should correctly read:
...
if Standard."="(L, null) or Standard."="(R, null) then -- universal =
return Standard."="(L, R); -- universal =
elsif L.Value = R.Value then
return L.Next = R.Next; -- recurses OK
...
As Simon Wright pointed out, GNAT seems to run into "unbounded recursion" with the original example, which is actually the correct compiler behavior according to the standard. However SO user G_Zeus mentioned that GNAT issues an ambiguity error for L = R in a similar example, which is incorrect compiler behavior. It should have unambiguously picked the overloaded operator =.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-15 8:51 ` daicrkk
@ 2019-01-15 11:15 ` Simon Wright
0 siblings, 0 replies; 17+ messages in thread
From: Simon Wright @ 2019-01-15 11:15 UTC (permalink / raw)
daicrkk@googlemail.com writes:
> if Standard."="(L, null) or Standard."="(R, null) then -- universal =
> return Standard."="(L, R); -- universal =
In my copy of Barnes's book (Programming in Ada 2012) in 11.7, at the
top of p.214, this is stated as the version required by Ada 2005 and to
be "a bit ugly".
One would rather be ugly than wrong.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-14 23:08 ` Overloading operator "=" " Randy Brukardt
` (2 preceding siblings ...)
2019-01-15 8:51 ` daicrkk
@ 2019-01-17 3:20 ` Jere
2019-01-17 8:23 ` Dmitry A. Kazakov
2019-01-17 22:22 ` Randy Brukardt
3 siblings, 2 replies; 17+ messages in thread
From: Jere @ 2019-01-17 3:20 UTC (permalink / raw)
On Monday, January 14, 2019 at 6:08:36 PM UTC-5, Randy Brukardt wrote:
> As always, best avoid anonymous access types unless you have to use one of
> their special features (dynamic accessibility, dispatching, special
> discriminant accessibility, or closures [for access-to-subprograms]). And
> better still, lets lobby to get those special features optionally available
> for named access types so no one every has to use an anonymous anything. :-)
>
> Randy.
I mostly find myself using them in situations where I need to save an
access variable of an object that doesn't live as long as the access
type involved. I can't do that safely with named access types. For
example, using them in containers (need the access type for iterators,
cursors, etc.) but creating those container objects inside an operation
of some form. I feel like having to use Unchecked_Access somewhat
defeats the purpose of using Ada (not all of it, just some).
Are there any plans to add smarter and more relaxed named access types
in the future? Something akin to rust references (especially with
the recently released non lexical lifetimes update)? It's definitely
possible to track references in such a way though I wouldn't claim
it was easy to do.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-17 3:20 ` Jere
@ 2019-01-17 8:23 ` Dmitry A. Kazakov
2019-01-17 22:22 ` Randy Brukardt
1 sibling, 0 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2019-01-17 8:23 UTC (permalink / raw)
On 2019-01-17 04:20, Jere wrote:
> I mostly find myself using them in situations where I need to save an
> access variable of an object that doesn't live as long as the access
> type involved. I can't do that safely with named access types. For
> example, using them in containers (need the access type for iterators,
> cursors, etc.)
Actually access discriminant meant to be long living, a mix-in hack to
avoid multiple inheritance implementation.
> but creating those container objects inside an operation
> of some form. I feel like having to use Unchecked_Access somewhat
> defeats the purpose of using Ada (not all of it, just some).
That is yet another hack. You should have been able to use a limited
type object as a discriminant straight away without pointers. The
situation is just same as with access to subprogram types. You would not
need them most of the time, if you could pass subprogram as a parameter.
> Are there any plans to add smarter and more relaxed named access types
> in the future? Something akin to rust references (especially with
> the recently released non lexical lifetimes update)? It's definitely
> possible to track references in such a way though I wouldn't claim
> it was easy to do.
There is no need in an access type in these situations, why naming it?
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-17 3:20 ` Jere
2019-01-17 8:23 ` Dmitry A. Kazakov
@ 2019-01-17 22:22 ` Randy Brukardt
2019-01-18 10:17 ` Dmitry A. Kazakov
2019-01-18 13:27 ` Jere
1 sibling, 2 replies; 17+ messages in thread
From: Randy Brukardt @ 2019-01-17 22:22 UTC (permalink / raw)
"Jere" <jhb.chat@gmail.com> wrote in message
news:252afc47-fe7d-41bc-b394-0735e1fd2e4c@googlegroups.com...
...
> I mostly find myself using them in situations where I need to save an
> access variable of an object that doesn't live as long as the access
> type involved. I can't do that safely with named access types.
You should have just stopped at "safely": "I can't do that safely." So far
as I know, the only way to do that with anonymous access (other than
parameters) is to used Unchecked_Access. And in the case of the parameters,
you should just have passed the designated object.
> For
> example, using them in containers (need the access type for iterators,
> cursors, etc.) but creating those container objects inside an operation
> of some form. I feel like having to use Unchecked_Access somewhat
> defeats the purpose of using Ada (not all of it, just some).
Ada accessibility checks are designed for a particular use case: that of
programs that cannot allow dynamically allocated objects but still need
accesses. In such a case, all of the objects are typically globally
allocated and managed in some way.
For most other uses, the best thing to do with accessibility checks is turn
them off. You have to dynamically manage lifetime in that case (usually
using a controlled type to do it), and accessibility provides nothing in
that case. And you turn off accessibility checks by using 'Unchecked_Access.
For the most part, I personally view accessibility checks as a mistake, but
then again I never write programs in the first category. So I have to take
others word for it that it is useful there. Dynamic accessibility provides
nothing but overhead and tripping hazards (the possibility of a mysterious
exception in otherwise tested code).
> Are there any plans to add smarter and more relaxed named access types
> in the future? Something akin to rust references (especially with
> the recently released non lexical lifetimes update)? It's definitely
> possible to track references in such a way though I wouldn't claim
> it was easy to do.
There won't be anything significant in Ada 2020; we're too close to the end
game for that to be able to do the experimentation with different designs
that is still needed. We have a few proposals for "smarter" access type, but
no one would call them "more relaxed". The basic idea is to use additional
compile-time and run-time restrictions to allow automatic deallocation and
to eliminate the possibility of dangling references.
I'm pretty sure there never will be "more relaxed" access types, as there is
a substantial community within Ada that would never allow making Ada
features less safe (that is, having more erroneous cases). I suppose one
could call a named access type that using only dynamic accessibility checks
"more relaxed", but all that would do is add a lot of overhead to access
types in order to convert a compile-time check into a runtime one (read new
way to have a program fail).
I'd like to see a way to allow named access types to have the various
special behaviors associated only with anonymous access types today, but I
certainly would not characterize that as "more relaxed".
Randy.
P.S. I agree with Dmitry that in many of these cases you don't want to use
access types at all. Most ADT interfaces are better off taking the ADT
objects directly; leave the client to organizing structures. When you use
access types in the interface, you force all clients to use access types in
their usage; it's better to let them decide whether scoping, containers, or
access types are the best way to manage the objects. (Definitely the most
reusable.) (And no, I don't consider forcing clients to use
'Unchecked_Access all over the place as a way for them to avoid using access
types.)
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-17 22:22 ` Randy Brukardt
@ 2019-01-18 10:17 ` Dmitry A. Kazakov
2019-01-18 13:27 ` Jere
1 sibling, 0 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2019-01-18 10:17 UTC (permalink / raw)
On 2019-01-17 23:22, Randy Brukardt wrote:
> Ada accessibility checks are designed for a particular use case: that of
> programs that cannot allow dynamically allocated objects but still need
> accesses.
A great succinct formulation everybody must keep in mind to avoid
misunderstandings!
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-17 22:22 ` Randy Brukardt
2019-01-18 10:17 ` Dmitry A. Kazakov
@ 2019-01-18 13:27 ` Jere
2019-01-18 13:42 ` Dmitry A. Kazakov
1 sibling, 1 reply; 17+ messages in thread
From: Jere @ 2019-01-18 13:27 UTC (permalink / raw)
On Thursday, January 17, 2019 at 5:22:49 PM UTC-5, Randy Brukardt wrote:
> "Jere" wrote in message
> ...
> > I mostly find myself using them in situations where I need to save an
> > access variable of an object that doesn't live as long as the access
> > type involved. I can't do that safely with named access types.
>
> You should have just stopped at "safely": "I can't do that safely." So far
> as I know, the only way to do that with anonymous access (other than
> parameters) is to used Unchecked_Access. And in the case of the parameters,
> you should just have passed the designated object.
It can be done safely, but just not using Ada's current paradigm. There
are other safety minded languages that have figured out how to do it
without needing to check against their equivalent of an access type's
lifetime. They can do it based on the lifetime of the actual
[their equivalent to] access object vs the lifetime of the object it
points to (at compile time).
>
> > For
> > example, using them in containers (need the access type for iterators,
> > cursors, etc.) but creating those container objects inside an operation
> > of some form. I feel like having to use Unchecked_Access somewhat
> > defeats the purpose of using Ada (not all of it, just some).
>
> Ada accessibility checks are designed for a particular use case: that of
> programs that cannot allow dynamically allocated objects but still need
> accesses. In such a case, all of the objects are typically globally
> allocated and managed in some way.
Which is particularly important in bare metal environments and smaller
embedded environments as well. Even just messing around with some custom
bounded containers leads me down this rabbit hole.
>
> <SNIPPED>
>
> > Are there any plans to add smarter and more relaxed named access types
> > in the future? Something akin to rust references (especially with
> > the recently released non lexical lifetimes update)? It's definitely
> > possible to track references in such a way though I wouldn't claim
> > it was easy to do.
>
> There won't be anything significant in Ada 2020; we're too close to the end
> game for that to be able to do the experimentation with different designs
> that is still needed. We have a few proposals for "smarter" access type, but
> no one would call them "more relaxed". The basic idea is to use additional
> compile-time and run-time restrictions to allow automatic deallocation and
> to eliminate the possibility of dangling references.
That would be nice. I think the biggest hurdle to named access types for
me is that the object has to live as long as the access type. That forces
you into using the equivalent of global variables a lot of time. At least
in Ada they have separate scopes to help with naming clashes, but it doesn't
protect you from having someone misuse a variable outside of its intent.
>
> I'm pretty sure there never will be "more relaxed" access types, as there is
> a substantial community within Ada that would never allow making Ada
> features less safe (that is, having more erroneous cases). I suppose one
> could call a named access type that using only dynamic accessibility checks
> "more relaxed", but all that would do is add a lot of overhead to access
> types in order to convert a compile-time check into a runtime one (read new
> way to have a program fail).
More relaxed doesn't necessarily mean less safe. It can even be more safe.
The less relaxed Ada paradigm can force you to provide a variable outside
of a scope that it is intended to be used in, which is a maintenance hazard.
>
> P.S. I agree with Dmitry that in many of these cases you don't want to use
> access types at all. Most ADT interfaces are better off taking the ADT
> objects directly; leave the client to organizing structures. When you use
> access types in the interface, you force all clients to use access types in
> their usage; it's better to let them decide whether scoping, containers, or
> access types are the best way to manage the objects. (Definitely the most
> reusable.) (And no, I don't consider forcing clients to use
> 'Unchecked_Access all over the place as a way for them to avoid using access
> types.)
I don't like using access types at all, but Ada requires it sometimes. Just
making a cursor or iterator to a custom container requires it. And if your
type is not limited, you start getting pushed more into unchecked_access
since you don't have handy tricks like the Rosen Technique. Conversely,
if you need to keep an array of dispatchable limited types, you have to use
access types (indefinite containers doesn't work on limited types). I
don't like to use them in public interfaces at all, so I avoid that, but it
is hard to avoid in implementation and harder still to ensure it is done
right. My hope is Ada eventually makes that both safe and more maintenance
friendly in the future, for the times where you have to use them.
Note, I am not saying that Ada has any serious problems, but am just
wishing there was better support for using access types with non heap
objects (for the times that you have to).
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Overloading operator "=" for anonymous access types?
2019-01-18 13:27 ` Jere
@ 2019-01-18 13:42 ` Dmitry A. Kazakov
0 siblings, 0 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2019-01-18 13:42 UTC (permalink / raw)
On 2019-01-18 14:27, Jere wrote:
> Note, I am not saying that Ada has any serious problems, but am just
> wishing there was better support for using access types with non heap
> objects (for the times that you have to).
No. Ada should not force access types where they are not needed. E.g.
the cases where a language-managed reference would suffice.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread