* Smart pointers and delegation
@ 2017-08-01 10:32 Dmitry A. Kazakov
2017-08-01 15:06 ` Dmitry A. Kazakov
2017-08-01 23:06 ` Randy Brukardt
0 siblings, 2 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-01 10:32 UTC (permalink / raw)
I would like to discuss delegation proposal as a replacement for crude,
inside out Implicit_Dereference aspect.
It goes as follows:
type I is interface ...
procedure Foo (X : I);
function Bar return I;
procedure Baz (X : in out I);
[anonymous access argument and result are handled like Foo and Bar]
type T is new I with ... record
...
end record
with Delegate[_In|_Out] => Relay;
-- May appear several times
Here Relay is one of the following:
1. discriminant or component of T (named or anonymous) of access to
I'Class or an expression of this result;
2. discriminant or component of T (named or anonymous) of access to
T'Class or an expression of this result;
3. function with the profile:
function Relay_In (X : T) return [constant] access [all] I'Class;
4. function with the profile:
function Relay_In (X : T) return I'Class;
5. procedure with the profile:
procedure Relay_Out (X : [in] out T; Y : [in] out I'Class);
For 1, 2, 3 the compiler overrides Foo, Bar, Baz with the following bodies:
procedure Foo (X : T) is -- If appears in Delegate[_In]
begin
X.Relay_In.Foo;
end Foo;
function Bar return T is -- If appears in Delegate[_In]
begin
return X.Relay_In.all;
end Bar;
procedure Baz (X : in out T) is -- If appears in Delegate and
begin -- not constant
X.Relay_In.Baz;
end Baz;
For 4 the compiler overrides Foo and Bar with the bodies like above. Baz
remains abstract.
procedure Foo (X : T) is -- If appears in Delegate[_In]
begin
X.Relay_In.Foo;
end Foo;
function Bar return T is -- If appears in Delegate[_In]
begin
return X.Relay_In;
end Bar;
For 5 the compiler overrides Baz with the body:
procedure Baz (X : in out T) is -- If Relay_In appears in Delegate_In
begin -- and Relay_Out does in Delegate_Out
Relay_Out (X, X.Relay_In.Baz);
end Baz;
Any overriding induced by the Delegate aspect happens only if the
corresponding operation has not yet been explicitly overridden.
Conflicting overriding per two Delegate aspects is an error.
-----------------------------------------------------------
Example 1. Smart pointers (handles)
type File_Interface is interface;
function Read (File : in out File_Interface)
return Stream_Element_Array;
procedure Write (File : in out File_Interface;
Data : Stream_Element_Array);
type File_Handle is new File_Interface with private;
private
type File_Descriptor is
new Ada.Finalization.Limited_Controlled
and File_Interface with
record
Use_Count : Natural := 0;
...
end record;
type File_Descriptor_Ptr is access File_Descriptor'Class;
type File_Handle is
new Ada.Finalization.Controlled
and File_Interface with
record
Descriptor : File_Descriptor_Ptr;
end record with Delegate => Descriptor;
--------------------------------------------------------------
Example 2. Full multiple inheritance
type A_Interface is limited interface;
type A is new A_Interface with ...;
type B is tagged ...;
type AB is new B and A_Interface with record
Alter_Ego : aliased A;
end record with Delegate => A'Access;
--------------------------------------------------------------
Example 3. Parallel pointers hierarchy
type A_Interface is limited interface;
type A_Implementation is new A_Interface with ...;
type A_Reference (Implementation : access A_Implementation'Class) is
new A_Interface with null record;
type B_Interface is limited interface;
type B_Implementation is new A and B_Interface with ...;
type B_Reference is new A_Reference and B_Interface with
null record
with Delegate =>
B_Imlementation'Class (Implementation.all)'Access;
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-01 10:32 Smart pointers and delegation Dmitry A. Kazakov
@ 2017-08-01 15:06 ` Dmitry A. Kazakov
2017-08-01 23:06 ` Randy Brukardt
1 sibling, 0 replies; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-01 15:06 UTC (permalink / raw)
On 2017-08-01 12:32, Dmitry A. Kazakov wrote:
> --------------------------------------------------------------
> Example 2. Full multiple inheritance
>
> type A_Interface is limited interface;
> type A is new A_Interface with ...;
>
> type B is tagged ...;
>
> type AB is new B and A_Interface with record
> Alter_Ego : aliased A;
> end record with Delegate => A'Access;
^^^^^^^^^
Alter_Ego'Access;
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-01 10:32 Smart pointers and delegation Dmitry A. Kazakov
2017-08-01 15:06 ` Dmitry A. Kazakov
@ 2017-08-01 23:06 ` Randy Brukardt
2017-08-02 6:20 ` Dmitry A. Kazakov
1 sibling, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-01 23:06 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:olplb6$jq0$1@gioia.aioe.org...
>I would like to discuss delegation proposal as a replacement for crude,
>inside out Implicit_Dereference aspect.
There never are "replacements", as existing Ada code has to work.
And as usual, you didn't explain the problem that you're trying to solve.
Implicit_Dereference was designed so a programmer can get control just
before and just after the use of a dereference. Indeed, it was a replacement
for an earlier idea where that was part of a storage pool. The problem with
the storage pool proposal was determining exactly where a dereference ends.
The description ended up similar to finalization, so someone suggested tying
the dereference to a controlled object, and Implicit_Dereference was born.
Implicit_Dereference very much matches the Ada model of "hooks" into
language-defined operations (much like Adjust and Finalize allow
user-defined operations to be associated with an assignment, rather than
replacing it).
The main intended use of this feature was to manage the lifetime of accessed
entities, such as a persistent store, without requiring copies of the
contained data (which might be large).
Your proposal doesn't meet this particular need, because there is no way to
tell when the program is finished with a function result. Thus, you either
have to copy all of the function results (which is what Ada does), which is
already determined to be too expensive, or you have to return an unsafe
access to the object (unsafe because you have no way to find out when the
access is no longer needed by the program).
It seems to me that you are trying to solve some other problem, rather than
the low-level one that Implicit_Dereference is trying to solve. Perhaps it
would be a good idea to articulate the problem before outlining the
solution??
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-01 23:06 ` Randy Brukardt
@ 2017-08-02 6:20 ` Dmitry A. Kazakov
2017-08-03 3:36 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-02 6:20 UTC (permalink / raw)
On 2017-08-02 01:06, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:olplb6$jq0$1@gioia.aioe.org...
>> I would like to discuss delegation proposal as a replacement for crude,
>> inside out Implicit_Dereference aspect.
>
> There never are "replacements", as existing Ada code has to work.
It is not replacement in that sense. Replacement here applies to the
"use case", i.e. creating subtypes. Implicit dereferencing effectively
makes pointer a "subtype" of the target type.
> And as usual, you didn't explain the problem that you're trying to solve.
The problem is subtyping and automated creation of wrappers to implement
"trivial" operations. The method is delegation. Examples are presented.
> Implicit_Dereference was designed so a programmer can get control just
> before and just after the use of a dereference.
Certainly not. There is no prologue or epilogue hooks, just one ugly
access type exposed. Should have been any function or expression
instead. But it is irrelevant because there is a lot of unwanted and
damaging stuff that comes with implicit dereferencing making it unusable
for the use cases in question. Like exposing the target type, which is
no-no for handle/proxy types. Like making reference objects indefinite etc.
> Implicit_Dereference very much matches the Ada model of "hooks" into
> language-defined operations (much like Adjust and Finalize allow
> user-defined operations to be associated with an assignment, rather than
> replacing it).
? Not even close. Adjust and Finalize are primitive operations, which
was a horrific design choice, but nevertheless. Implicit dereference is
not an operation, not even a body.
> The main intended use of this feature was to manage the lifetime of accessed
> entities, such as a persistent store, without requiring copies of the
> contained data (which might be large).
Copies are very essential in many cases, e.g. an external reference to a
database table row etc. It is up to the programmer to define the semantics.
> Your proposal doesn't meet this particular need, because there is no way to
> tell when the program is finished with a function result.
I don't understand what you mean. The proposal considers exclusively how
to generate bodies of normal primitive operations. All current rules
apply to them, no any new rules needed.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-02 6:20 ` Dmitry A. Kazakov
@ 2017-08-03 3:36 ` Randy Brukardt
2017-08-03 7:40 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-03 3:36 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:olrqum$1t4b$1@gioia.aioe.org...
> On 2017-08-02 01:06, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:olplb6$jq0$1@gioia.aioe.org...
>>> I would like to discuss delegation proposal as a replacement for crude,
>>> inside out Implicit_Dereference aspect.
>>
>> There never are "replacements", as existing Ada code has to work.
>
> It is not replacement in that sense. Replacement here applies to the "use
> case", i.e. creating subtypes. Implicit dereferencing effectively makes
> pointer a "subtype" of the target type.
As I noted at the end of my message, your proposal does not replace the "use
case" for Implicit_Dereference. So this isn't true.
>> And as usual, you didn't explain the problem that you're trying to solve.
>
> The problem is subtyping and automated creation of wrappers to implement
> "trivial" operations. The method is delegation. Examples are presented.
I think you need to concentrate on describing that problem rather than any
particular solution to solve it. It doesn't seem like a problem that is
really solveable in the general case. (Certainly if you want to make some
sort of proposal; not so much if you are just making talk here.)
...
>> Implicit_Dereference very much matches the Ada model of "hooks" into
>> language-defined operations (much like Adjust and Finalize allow
>> user-defined operations to be associated with an assignment, rather than
>> replacing it).
>
> ? Not even close. Adjust and Finalize are primitive operations, which was
> a horrific design choice, but nevertheless. Implicit dereference is not an
> operation, not even a body.
In the intended use, Implicit_Dereference is always combined with a
controlled type, which provides the actual hooks. As I noted yesterday, the
original idea was specifically for explicit hooks (as part of the storage
pool mechanism, similar to Adjust and Finalize), but that got replaced when
it was pointed out that one could already write the needed support using an
access discriminant and associated controlled type. That got rid of the need
to define new mechanisms and deal with the inevitable corner cases. The only
problem with that was that it was unspeakably ugly, so Implicit_Dereference
was created to get rid of that without adding any real new semantics.
Any other use of Implicit_Dereference is purely accidental; no other use
provides anything that can't be written directly in Ada 95.
>> The main intended use of this feature was to manage the lifetime of
>> accessed
>> entities, such as a persistent store, without requiring copies of the
>> contained data (which might be large).
>
> Copies are very essential in many cases, e.g. an external reference to a
> database table row etc. It is up to the programmer to define the
> semantics.
If you want (or don't care about copies) copies, normal primitive functions
work great. See, for instance, function Element in the various containers
packages. The access discriminant/controlled type/Implicit_Dereference
mechanism is intended for those cases where a copy is not usable (either
because of semantics or because of execution cost).
>> Your proposal doesn't meet this particular need, because there is no way
>> to
>> tell when the program is finished with a function result.
>
> I don't understand what you mean. The proposal considers exclusively how
> to generate bodies of normal primitive operations. All current rules apply
> to them, no any new rules needed.
That's the problem: the Implicit_Dereference mechanism is intended for the
rare cases where "normal primitive operations" don't work (because they
imply copies for function results). If "normal primitive operations" work,
you don't need (and shouldn't use) any special mechanism.
From your description, you're trying to solve an altogether different
problem. That's fine, of course, but just don't confuse that with the rather
low-level problem that Implicit_Dereference was actually trying to solve.
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-03 3:36 ` Randy Brukardt
@ 2017-08-03 7:40 ` Dmitry A. Kazakov
2017-08-04 23:03 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-03 7:40 UTC (permalink / raw)
On 2017-08-03 05:36, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:olrqum$1t4b$1@gioia.aioe.org...
>> On 2017-08-02 01:06, Randy Brukardt wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>> news:olplb6$jq0$1@gioia.aioe.org...
>>>> I would like to discuss delegation proposal as a replacement for crude,
>>>> inside out Implicit_Dereference aspect.
>>>
>>> There never are "replacements", as existing Ada code has to work.
>>
>> It is not replacement in that sense. Replacement here applies to the "use
>> case", i.e. creating subtypes. Implicit dereferencing effectively makes
>> pointer a "subtype" of the target type.
>
> As I noted at the end of my message, your proposal does not replace the "use
> case" for Implicit_Dereference. So this isn't true.
>
>>> And as usual, you didn't explain the problem that you're trying to solve.
>>
>> The problem is subtyping and automated creation of wrappers to implement
>> "trivial" operations. The method is delegation. Examples are presented.
>
> I think you need to concentrate on describing that problem rather than any
> particular solution to solve it. It doesn't seem like a problem that is
> really solveable in the general case. (Certainly if you want to make some
> sort of proposal; not so much if you are just making talk here.)
I have no illusions (:-)) So talk would be just fine. Especially if in
the end the concept catches interest. Delegation is a very powerful
mechanism which requires very little if any language tampering. It is a
very nice alternative for generics which does not generate a whole
second language tier unreadable and unmaintainable.
>>> Implicit_Dereference very much matches the Ada model of "hooks" into
>>> language-defined operations (much like Adjust and Finalize allow
>>> user-defined operations to be associated with an assignment, rather than
>>> replacing it).
>>
>> ? Not even close. Adjust and Finalize are primitive operations, which was
>> a horrific design choice, but nevertheless. Implicit dereference is not an
>> operation, not even a body.
>
> In the intended use, Implicit_Dereference is always combined with a
> controlled type, which provides the actual hooks.
? These are not hooks on the operation of dereferencing.
[E.g. you cannot make a task-safe dereferencing by taking a lock at the
beginning and releasing it at the end, i.e. a typical Ref/Unref pair]
[...]
>>> Your proposal doesn't meet this particular need, because there is no way
>>> to tell when the program is finished with a function result.
>>
>> I don't understand what you mean. The proposal considers exclusively how
>> to generate bodies of normal primitive operations. All current rules apply
>> to them, no any new rules needed.
>
> That's the problem: the Implicit_Dereference mechanism is intended for the
> rare cases where "normal primitive operations" don't work (because they
> imply copies for function results). If "normal primitive operations" work,
> you don't need (and shouldn't use) any special mechanism.
I must and I need it, because in real-life examples (e.g. #1) it is many
thousands lines of noise code in both package declarations and bodies.
Which is quite error-prone too.
> From your description, you're trying to solve an altogether different
> problem. That's fine, of course, but just don't confuse that with the rather
> low-level problem that Implicit_Dereference was actually trying to solve.
Yes it is a wider problem, but one use-case is exactly same. The
solution is different:
1. Dereference:
type Target_Interface is interface;
type Reference (Pointer : access Target_Interface) is null record
with Implicit_Dereference => Pointer;
2. Delegation:
type Target_Interface is interface;
type Reference is new Target_Interface record
Pointer : access Target_Interface'Class
end record
with Delegate => Pointer;
Delegation has no magic whatsoever. It could be done by an ASIS-aware
preprocessor actually.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-03 7:40 ` Dmitry A. Kazakov
@ 2017-08-04 23:03 ` Randy Brukardt
2017-08-05 8:33 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-04 23:03 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:oluk0l$a8o$1@gioia.aioe.org...
> On 2017-08-03 05:36, Randy Brukardt wrote:
...
>>> ? Not even close. Adjust and Finalize are primitive operations, which
>>> was
>>> a horrific design choice, but nevertheless. Implicit dereference is not
>>> an
>>> operation, not even a body.
>>
>> In the intended use, Implicit_Dereference is always combined with a
>> controlled type, which provides the actual hooks.
>
> ? These are not hooks on the operation of dereferencing.
>
> [E.g. you cannot make a task-safe dereferencing by taking a lock at the
> beginning and releasing it at the end, i.e. a typical Ref/Unref pair]
Why not? That's precisely the sort of use that is intended. One seizes the
lock when the dereference object is created (the start of the dereference)
and the frees it when the dereference object is destroyed (finalized).
> [...]
>> That's the problem: the Implicit_Dereference mechanism is intended for
>> the
>> rare cases where "normal primitive operations" don't work (because they
>> imply copies for function results). If "normal primitive operations"
>> work,
>> you don't need (and shouldn't use) any special mechanism.
>
> I must and I need it, because in real-life examples (e.g. #1) it is many
> thousands lines of noise code in both package declarations and bodies.
> Which is quite error-prone too.
Sorry, I meant the mechanism as intended is only needed in limited cases.
You have a very different use-case that has nothing whatsoever to do with
the need for the Implicit_Dereference mechanism. I didn't intend to say
anything about the value of that use-case.
Indeed, the proposal you made (as best as I understand it) only used "normal
primitive operations"; the idea was to automatically construct them in some
cases. Nothing wrong that I can see with that idea, but it doesn't have
anything to do with Implicit_Dereference as intended. (You'd still need a
mechanism like that for uses where copies can't be used, assuming you care
about safety.)
>> From your description, you're trying to solve an altogether different
>> problem. That's fine, of course, but just don't confuse that with the
>> rather
>> low-level problem that Implicit_Dereference was actually trying to solve.
>
> Yes it is a wider problem, but one use-case is exactly same. The solution
> is different:
>
> 1. Dereference:
>
> type Target_Interface is interface;
> type Reference (Pointer : access Target_Interface) is null record
> with Implicit_Dereference => Pointer;
>
> 2. Delegation:
>
> type Target_Interface is interface;
> type Reference is new Target_Interface record
> Pointer : access Target_Interface'Class
> end record
> with Delegate => Pointer;
>
> Delegation has no magic whatsoever. It could be done by an ASIS-aware
> preprocessor actually.
The problem is that the delegation is unsafe -- it has very different
semantics. The use of an access discriminant in Implicit_Dereference is what
gives the limited lifetime to the access type, and that makes it possible to
use finalization to get control at the end of the life of the dereference.
You can't do that with an ordinary component, which has no limits on the
lifetime of the dereference. And there is nowhere obvious to hang the "end
dereference" hook.
That could be fixed with additional new features, I suppose, but the rules
for access discriminants are such a mess that trying to repeat them in some
way is not an appealing project (one of the reasons that
Implicit_Dereference ended up like it did.)
Ergo, (2) is not in any way a replacement for (1). Unless (1) is being used
without the controlled object and finalization "hook" -- but there is no
reason whatsoever to do that (you can just use a regular access value in
that case).
Randy.
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-04 23:03 ` Randy Brukardt
@ 2017-08-05 8:33 ` Dmitry A. Kazakov
2017-08-07 22:39 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-05 8:33 UTC (permalink / raw)
On 2017-08-05 01:03, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:oluk0l$a8o$1@gioia.aioe.org...
>> On 2017-08-03 05:36, Randy Brukardt wrote:
> ...
>>>> ? Not even close. Adjust and Finalize are primitive operations, which
>>>> was
>>>> a horrific design choice, but nevertheless. Implicit dereference is not
>>>> an
>>>> operation, not even a body.
>>>
>>> In the intended use, Implicit_Dereference is always combined with a
>>> controlled type, which provides the actual hooks.
>>
>> ? These are not hooks on the operation of dereferencing.
>>
>> [E.g. you cannot make a task-safe dereferencing by taking a lock at the
>> beginning and releasing it at the end, i.e. a typical Ref/Unref pair]
>
> Why not? That's precisely the sort of use that is intended. One seizes the
> lock when the dereference object is created (the start of the dereference)
> and the frees it when the dereference object is destroyed (finalized).
What is dereference object? There is a target object X and reference
object R, both already exist.
Let I want to pass R to an operation F of X. This should lock/unlock on
the way. I cannot do that without some third type.
>> [...]
>>> That's the problem: the Implicit_Dereference mechanism is intended for the
>>> rare cases where "normal primitive operations" don't work (because they
>>> imply copies for function results). If "normal primitive operations" work,
>>> you don't need (and shouldn't use) any special mechanism.
>>
>> I must and I need it, because in real-life examples (e.g. #1) it is many
>> thousands lines of noise code in both package declarations and bodies.
>> Which is quite error-prone too.
>
> Sorry, I meant the mechanism as intended is only needed in limited cases.
> You have a very different use-case that has nothing whatsoever to do with
> the need for the Implicit_Dereference mechanism. I didn't intend to say
> anything about the value of that use-case.
Implicit dereferencing semantically does the same. For each visible
operation of the target type it declares an anonymous operation of the
reference type that dereferences and then calls to the original operation.
Delegation has these operations named. [and primitive. In this sense it
is more restricted]
> Indeed, the proposal you made (as best as I understand it) only used "normal
> primitive operations"; the idea was to automatically construct them in some
> cases. Nothing wrong that I can see with that idea, but it doesn't have
> anything to do with Implicit_Dereference as intended. (You'd still need a
> mechanism like that for uses where copies can't be used, assuming you care
> about safety.)
>
>>> From your description, you're trying to solve an altogether different
>>> problem. That's fine, of course, but just don't confuse that with the
>>> rather
>>> low-level problem that Implicit_Dereference was actually trying to solve.
>>
>> Yes it is a wider problem, but one use-case is exactly same. The solution
>> is different:
>>
>> 1. Dereference:
>>
>> type Target_Interface is interface;
>> type Reference (Pointer : access Target_Interface) is null record
>> with Implicit_Dereference => Pointer;
>>
>> 2. Delegation:
>>
>> type Target_Interface is interface;
>> type Reference is new Target_Interface record
>> Pointer : access Target_Interface'Class
>> end record
>> with Delegate => Pointer;
>>
>> Delegation has no magic whatsoever. It could be done by an ASIS-aware
>> preprocessor actually.
>
> The problem is that the delegation is unsafe -- it has very different
> semantics. The use of an access discriminant in Implicit_Dereference is what
> gives the limited lifetime to the access type, and that makes it possible to
> use finalization to get control at the end of the life of the dereference.
It could be an access discriminant too. But I used an access component
to illustrate that it need not to be a discriminant.
The use-case is reference counting smart pointers. I guess you rather
meant a temporary reference into some container when the reference is
short-lived and the container element is long-lived.
Smart pointers use the opposite approach. Pointers (the last one)
outlive the targets. If a container is built then out of smart pointers,
e.g. it keeps pointers to the elements. Alternatively, it can keep raw
elements and hold a reference to each. You access elements strictly
through pointers which need not to be disposed immediately. They can
live as long as necessary. It can be used in transactional schemes, e.g.
cloning elements for mutators if the reference count > 1 etc.
> You can't do that with an ordinary component, which has no limits on the
> lifetime of the dereference. And there is nowhere obvious to hang the "end
> dereference" hook.
(I must know what dereference is)
There are cases which do not involve access types. E.g.
multiple-inheritance for poor:
type File_Interface is limited interface;
type File_Type is new File_Interface with ...;
type Widget is ...;
type File_Selector_Widget is
new Widget and File_Interface with
record
Selected_File : File_Type;
end record with Delegate => Selected_File;
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-05 8:33 ` Dmitry A. Kazakov
@ 2017-08-07 22:39 ` Randy Brukardt
2017-08-08 6:27 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-07 22:39 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:om3vsc$jhk$1@gioia.aioe.org...
> On 2017-08-05 01:03, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:oluk0l$a8o$1@gioia.aioe.org...
>>> On 2017-08-03 05:36, Randy Brukardt wrote:
>> ...
>>>>> ? Not even close. Adjust and Finalize are primitive operations, which
>>>>> was
>>>>> a horrific design choice, but nevertheless. Implicit dereference is
>>>>> not
>>>>> an
>>>>> operation, not even a body.
>>>>
>>>> In the intended use, Implicit_Dereference is always combined with a
>>>> controlled type, which provides the actual hooks.
>>>
>>> ? These are not hooks on the operation of dereferencing.
>>>
>>> [E.g. you cannot make a task-safe dereferencing by taking a lock at the
>>> beginning and releasing it at the end, i.e. a typical Ref/Unref pair]
>>
>> Why not? That's precisely the sort of use that is intended. One seizes
>> the
>> lock when the dereference object is created (the start of the
>> dereference)
>> and the frees it when the dereference object is destroyed (finalized).
>
> What is dereference object? There is a target object X and reference
> object R, both already exist.
>
> Let I want to pass R to an operation F of X. This should lock/unlock on
> the way. I cannot do that without some third type.
Right. And that's a problem how? Helper types are quite common in ADTs.
>>> [...]
>>>> That's the problem: the Implicit_Dereference mechanism is intended for
>>>> the
>>>> rare cases where "normal primitive operations" don't work (because they
>>>> imply copies for function results). If "normal primitive operations"
>>>> work,
>>>> you don't need (and shouldn't use) any special mechanism.
>>>
>>> I must and I need it, because in real-life examples (e.g. #1) it is many
>>> thousands lines of noise code in both package declarations and bodies.
>>> Which is quite error-prone too.
>>
>> Sorry, I meant the mechanism as intended is only needed in limited cases.
>> You have a very different use-case that has nothing whatsoever to do with
>> the need for the Implicit_Dereference mechanism. I didn't intend to say
>> anything about the value of that use-case.
>
> Implicit dereferencing semantically does the same. For each visible
> operation of the target type it declares an anonymous operation of the
> reference type that dereferences and then calls to the original operation.
I don't see this at all.
> Delegation has these operations named. [and primitive. In this sense it is
> more restricted]
Nothing wrong with that per-se, but I fail to see the connection.
...
>> The problem is that the delegation is unsafe -- it has very different
>> semantics. The use of an access discriminant in Implicit_Dereference is
>> what
>> gives the limited lifetime to the access type, and that makes it possible
>> to
>> use finalization to get control at the end of the life of the
>> dereference.
>
> It could be an access discriminant too. But I used an access component to
> illustrate that it need not to be a discriminant.
>
> The use-case is reference counting smart pointers. I guess you rather
> meant a temporary reference into some container when the reference is
> short-lived and the container element is long-lived.
Correct. That was the problematic case.
> Smart pointers use the opposite approach. Pointers (the last one) outlive
> the targets. If a container is built then out of smart pointers, e.g. it
> keeps pointers to the elements. Alternatively, it can keep raw elements
> and hold a reference to each. You access elements strictly through
> pointers which need not to be disposed immediately. They can live as long
> as necessary. It can be used in transactional schemes, e.g. cloning
> elements for mutators if the reference count > 1 etc.
It's not really opposite, though, if you want to limit the lifetime of the
reference (even if that limit is potentially a long time).
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-07 22:39 ` Randy Brukardt
@ 2017-08-08 6:27 ` Dmitry A. Kazakov
2017-08-09 0:27 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-08 6:27 UTC (permalink / raw)
On 2017-08-08 00:39, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:om3vsc$jhk$1@gioia.aioe.org...
>> Let I want to pass R to an operation F of X. This should lock/unlock on
>> the way. I cannot do that without some third type.
>
> Right. And that's a problem how? Helper types are quite common in ADTs.
Everything is wrong. Helper type indicates language design problem. Each
and every type must reflect some entity in the problem space. Helper
type, as the name suggests, reflects a language problem instead.
>> Implicit dereferencing semantically does the same. For each visible
>> operation of the target type it declares an anonymous operation of the
>> reference type that dereferences and then calls to the original operation.
>
> I don't see this at all.
You do it with the eyes closed... (:-))
>> Smart pointers use the opposite approach. Pointers (the last one) outlive
>> the targets. If a container is built then out of smart pointers, e.g. it
>> keeps pointers to the elements. Alternatively, it can keep raw elements
>> and hold a reference to each. You access elements strictly through
>> pointers which need not to be disposed immediately. They can live as long
>> as necessary. It can be used in transactional schemes, e.g. cloning
>> elements for mutators if the reference count > 1 etc.
>
> It's not really opposite, though, if you want to limit the lifetime of the
> reference (even if that limit is potentially a long time).
Why would I want this, if the reference manages the object? The cardinal
difference is between object-managed references and reference-managed
objects. The former has a very limited use and would prefer to have all
of them built-in.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-08 6:27 ` Dmitry A. Kazakov
@ 2017-08-09 0:27 ` Randy Brukardt
2017-08-09 7:37 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-09 0:27 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:omblk3$svi$1@gioia.aioe.org...
> On 2017-08-08 00:39, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:om3vsc$jhk$1@gioia.aioe.org...
...
>>> Implicit dereferencing semantically does the same. For each visible
>>> operation of the target type it declares an anonymous operation of the
>>> reference type that dereferences and then calls to the original
>>> operation.
>>
>> I don't see this at all.
>
> You do it with the eyes closed... (:-))
That's possible. I'm not the best at seeing patterns; I'm much more likely
to write everything from scratch. Thus I don't find use for interfaces, for
one example.
...
>> It's not really opposite, though, if you want to limit the lifetime of
>> the
>> reference (even if that limit is potentially a long time).
>
> Why would I want this, if the reference manages the object? The cardinal
> difference is between object-managed references and reference-managed
> objects. The former has a very limited use and would prefer to have all of
> them built-in.
In that case, you definitely need to know precisely when the reference dies,
so you can "manage the object". If you have a lifetime leak (such as any
normal access type does), then you can't do that management safely. The
pattern of the intended use-case of Implicit_Dereference does exactly this.
Yes, I agree that it would have been better to have an explicit call-back
(that was the original idea, after all), but that was a lot more complex in
language and implementation terms. I don't think the complex version was
going to fly, and the simple version solves the problem that we needed to
solve. Sometimes, a "meh" idea that you can actually get implemented is
better than a brilliant idea that doesn't get any traction and thus never
actually exists anywhere.
Randy.
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-09 0:27 ` Randy Brukardt
@ 2017-08-09 7:37 ` Dmitry A. Kazakov
2017-08-09 22:57 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-09 7:37 UTC (permalink / raw)
On 2017-08-09 02:27, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:omblk3$svi$1@gioia.aioe.org...
>> Why would I want this, if the reference manages the object? The cardinal
>> difference is between object-managed references and reference-managed
>> objects. The former has a very limited use and would prefer to have all of
>> them built-in.
>
> In that case, you definitely need to know precisely when the reference dies,
> so you can "manage the object".
Right. That is the advantage of the approach. Reference's life-time is
better defined than the object's one. E.g. references can be scoped when
the object cannot. Or, more importantly, when the object must stay
hidden or is inaccessible like system resource or remote objects.
> If you have a lifetime leak (such as any
> normal access type does), then you can't do that management safely.
It happens when references are components of other objects and/or have
circular dependencies. Therefore strong and weak references go.
> The pattern of the intended use-case of Implicit_Dereference does exactly this.
It cannot replace smart pointer, because it does not manage the object.
So the question is whether implicit dereference can be a part of smart
pointer:
target <-- smart pointer with implicit dereference
This does not work for multiple reasons either. So the solution could
only be ugly heavy-weighted:
target <-- smart pointer
|| |
|| | operation to produce helper object
|| V
target <-- helper object with implicit dereference
This has no advantages whatsoever. Ergo, implicit dereference has no use
for smart pointers.
P.S. This is what happens with solutions of "real" problems...
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-09 7:37 ` Dmitry A. Kazakov
@ 2017-08-09 22:57 ` Randy Brukardt
2017-08-10 7:56 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-09 22:57 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:omee2p$145a$1@gioia.aioe.org...
> On 2017-08-09 02:27, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:omblk3$svi$1@gioia.aioe.org...
>
>>> Why would I want this, if the reference manages the object? The cardinal
>>> difference is between object-managed references and reference-managed
>>> objects. The former has a very limited use and would prefer to have all
>>> of
>>> them built-in.
>>
>> In that case, you definitely need to know precisely when the reference
>> dies,
>> so you can "manage the object".
>
> Right. That is the advantage of the approach. Reference's life-time is
> better defined than the object's one. E.g. references can be scoped when
> the object cannot. Or, more importantly, when the object must stay hidden
> or is inaccessible like system resource or remote objects.
>
>> If you have a lifetime leak (such as any
>> normal access type does), then you can't do that management safely.
>
> It happens when references are components of other objects and/or have
> circular dependencies. Therefore strong and weak references go.
>
>> The pattern of the intended use-case of Implicit_Dereference does exactly
>> this.
>
> It cannot replace smart pointer, because it does not manage the object. So
> the question is whether implicit dereference can be a part of smart
> pointer:
>
> target <-- smart pointer with implicit dereference
>
> This does not work for multiple reasons either.
I find this surprising. It works in the use-cases I've tried. (I haven't
tried a pure Smart-Pointer, although others have, since I prefer to include
such things in a larger abstract - like a container).
...
> So the solution could only be ugly heavy-weighted:
>
> target <-- smart pointer
> || |
> || | operation to produce helper object
> || V
> target <-- helper object with implicit dereference
>
> This has no advantages whatsoever. Ergo, implicit dereference has no use
> for smart pointers.
Even if true, the "real problem" however remains. If you can safely and
efficiently copy the designated object, then you have no need for
Implicit_Dereference. If you need "return-by-reference", though, you need
some special mechanism to do that. We tried a variety of approaches, and
none of them worked out that well (there are 4 alternatives for
AI05-0142-1). Thus we ended up with Implicit_Dereference.
Perhaps in a brand-new language, it would be possible to make a sensible
definition of return-by-reference directly, but it doesn't make sense in Ada
given the existing model and requirements.
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-09 22:57 ` Randy Brukardt
@ 2017-08-10 7:56 ` Dmitry A. Kazakov
2017-08-11 0:17 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-10 7:56 UTC (permalink / raw)
On 2017-08-10 00:57, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:omee2p$145a$1@gioia.aioe.org...
>> It cannot replace smart pointer, because it does not manage the object. So
>> the question is whether implicit dereference can be a part of smart
>> pointer:
>>
>> target <-- smart pointer with implicit dereference
>>
>> This does not work for multiple reasons either.
>
> I find this surprising. It works in the use-cases I've tried. (I haven't
> tried a pure Smart-Pointer, although others have, since I prefer to include
> such things in a larger abstract - like a container).
It will have a discriminant, to start with. Smart pointer must be
aggregation-friendly.
>> So the solution could only be ugly heavy-weighted:
>>
>> target <-- smart pointer
>> || |
>> || | operation to produce helper object
>> || V
>> target <-- helper object with implicit dereference
>>
>> This has no advantages whatsoever. Ergo, implicit dereference has no use
>> for smart pointers.
>
> Even if true, the "real problem" however remains. If you can safely and
> efficiently copy the designated object, then you have no need for
> Implicit_Dereference. If you need "return-by-reference", though, you need
> some special mechanism to do that.
I don't want
A (I) := X;
taken literally. That is an abstraction inversion. I want it compiled
into more useful
Set (A, I, X);
My design of containers is that I effectively put a reference in the
form of a smart pointer into the container. I don't need return by
reference because I return a smart pointer, which is definite and
non-limited. I have no problem with life time because the reference
holds the object. I less problems with concurrent updates because the
reference has a count so that the update may deploy a transaction
scheme. And I tend to hide the referenced object type from public
interface to make it more safe.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-10 7:56 ` Dmitry A. Kazakov
@ 2017-08-11 0:17 ` Randy Brukardt
2017-08-11 6:43 ` Dmitry A. Kazakov
0 siblings, 1 reply; 17+ messages in thread
From: Randy Brukardt @ 2017-08-11 0:17 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:omh3iv$1flt$1@gioia.aioe.org...
> On 2017-08-10 00:57, Randy Brukardt wrote:
...
>> Even if true, the "real problem" however remains. If you can safely and
>> efficiently copy the designated object, then you have no need for
>> Implicit_Dereference. If you need "return-by-reference", though, you need
>> some special mechanism to do that.
>
> I don't want
>
> A (I) := X;
>
> taken literally. That is an abstraction inversion. I want it compiled into
> more useful
>
> Set (A, I, X);
But these are subtly different. The procedure form can only update the
entire element at once. (Given the potentially large number of
subcomponents, it would be impractical to implement a procedure for each
one -- and if you want the compiler to do it, you'd have to be able to
write - within the language - how that's done, which brings back the
original question.)
Specifically, if you write:
A (I).C := Y;
there isn't any translation using Set that would do the right thing. (Using
an implicit temporary for the entire object would force extra copying, which
would have performance and tasking implications - it would be outright wrong
for volatile/atomic objects.)
> My design of containers is that I effectively put a reference in the form
> of a smart pointer into the container. I don't need return by reference
> because I return a smart pointer, which is definite and non-limited. I
> have no problem with life time because the reference holds the object. I
> less problems with concurrent updates because the reference has a count so
> that the update may deploy a transaction scheme. And I tend to hide the
> referenced object type from public interface to make it more safe.
Not sure I see how a "smart pointer" would work in this context. I could
imagine some sort of handle working if you don't need partial, in-place
updates. To do that, you have to merge the object interface and the
container interface; as I noted I sometimes have done designs that way, I
just don't call the handle a "smart pointer".
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-11 0:17 ` Randy Brukardt
@ 2017-08-11 6:43 ` Dmitry A. Kazakov
2017-08-11 20:37 ` Randy Brukardt
0 siblings, 1 reply; 17+ messages in thread
From: Dmitry A. Kazakov @ 2017-08-11 6:43 UTC (permalink / raw)
On 2017-08-11 02:17, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:omh3iv$1flt$1@gioia.aioe.org...
>> On 2017-08-10 00:57, Randy Brukardt wrote:
> ...
>>> Even if true, the "real problem" however remains. If you can safely and
>>> efficiently copy the designated object, then you have no need for
>>> Implicit_Dereference. If you need "return-by-reference", though, you need
>>> some special mechanism to do that.
>>
>> I don't want
>>
>> A (I) := X;
>>
>> taken literally. That is an abstraction inversion. I want it compiled into
>> more useful
>>
>> Set (A, I, X);
>
> But these are subtly different. The procedure form can only update the
> entire element at once. (Given the potentially large number of
> subcomponents, it would be impractical to implement a procedure for each
> one -- and if you want the compiler to do it, you'd have to be able to
> write - within the language - how that's done, which brings back the
> original question.)
>
> Specifically, if you write:
> A (I).C := Y;
> there isn't any translation using Set that would do the right thing. (Using
> an implicit temporary for the entire object would force extra copying, which
> would have performance and tasking implications - it would be outright wrong
> for volatile/atomic objects.)
This is why I am looking for delegation methods in order to route
operation ".C :=" from element type to the container type:
Set_C (A, I, Y);
This of course applies to all operations on container elements.
>> My design of containers is that I effectively put a reference in the form
>> of a smart pointer into the container. I don't need return by reference
>> because I return a smart pointer, which is definite and non-limited. I
>> have no problem with life time because the reference holds the object. I
>> less problems with concurrent updates because the reference has a count so
>> that the update may deploy a transaction scheme. And I tend to hide the
>> referenced object type from public interface to make it more safe.
>
> Not sure I see how a "smart pointer" would work in this context. I could
> imagine some sort of handle working if you don't need partial, in-place
> updates.
Partial updates work because there an explicit operation to get plain
access type:
A (I).Ptr.C := Y; -- [*]
But I don't like this sort of things anyway. So usually it is a setter
defined on the handle:
A (I).Set_C (Y);
Which is why the handle and the target type implement same interface:
type Element_Interface is interface;
procedure Set_C (X : Element_Interface; Y : Integer);
And again I need delegation to automate trivial implementations of the
interface:
procedure Set_C (X : Handle; Y : Integer) is
begin
X.Ptr.Set_C (Y);
end Set_C;
which presently I have to write manually.
-----------------------------
* I really do not understand why Implict_Dereference has this ugly form
instead of simply declaring that type P is a user implementation of
access-to-T type with user operation to provide access-to-T result. BTW,
a type can be both access-to-T and access-to-V. Why not? But this is
another story because smart pointers need not to be access, not visibly.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: Smart pointers and delegation
2017-08-11 6:43 ` Dmitry A. Kazakov
@ 2017-08-11 20:37 ` Randy Brukardt
0 siblings, 0 replies; 17+ messages in thread
From: Randy Brukardt @ 2017-08-11 20:37 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:omjjlr$1ia7$1@gioia.aioe.org...
> On 2017-08-11 02:17, Randy Brukardt wrote:
...
>>> My design of containers is that I effectively put a reference in the
>>> form
>>> of a smart pointer into the container. I don't need return by reference
>>> because I return a smart pointer, which is definite and non-limited. I
>>> have no problem with life time because the reference holds the object. I
>>> less problems with concurrent updates because the reference has a count
>>> so
>>> that the update may deploy a transaction scheme. And I tend to hide the
>>> referenced object type from public interface to make it more safe.
>>
>> Not sure I see how a "smart pointer" would work in this context. I could
>> imagine some sort of handle working if you don't need partial, in-place
>> updates.
>
> Partial updates work because there an explicit operation to get plain
> access type:
>
> A (I).Ptr.C := Y; -- [*]
>
> But I don't like this sort of things anyway.
That's good, because a "plain access type" is dangerous (it has a global
lifetime if defined in an ADT). Thus it very easily becomes dangling. If all
you want is something for dereferences, you have to use an anonymous access
discriminant and a call-back when done, which is the normal usage pattern of
Implicit_Dereference (of course, you can do that without using
Implicit_Dereference if you don't mind the extra text as in the above
assignment).
> So usually it is a setter defined on the handle:
>
> A (I).Set_C (Y);
That's better, but of course requires a setter for every component. Which
necessarily merges the container and the element.
...
> -----------------------------
> * I really do not understand why Implict_Dereference has this ugly form
> instead of simply declaring that type P is a user implementation of
> access-to-T type with user operation to provide access-to-T result. BTW, a
> type can be both access-to-T and access-to-V. Why not? But this is another
> story because smart pointers need not to be access, not visibly.
"Simply"? Not much of anything is "simply" in language terms.
Implicit_Dereference is relatively simple in language terms because it is
mainly defined in terms of a text equivalence (thus, few rules have to be
repeated). I doubt anyone would argue that it is simple in usage terms; the
theory was that few would care about the details of setting it up, since the
vast majority would just use it in a container. That's very much like the
theory behind generics. (And yes, I doubt you would agree with that theory
in either case.)
No one suggested an idea like the one you gave here, so it certainly was not
considered. But typically, ambiguity in a type declaration causes problems
elsewhere. Types are the one thing in Ada that really provide an anchor:
they can't be overloaded, they always have a single meaning (at a particular
point in the text, it could change from visibility but nothing else), and so
on. Changing that might be problematical. But someone would have to do a
full analysis to be sure, and it is too late for that now.
Randy.
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2017-08-11 20:37 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-01 10:32 Smart pointers and delegation Dmitry A. Kazakov
2017-08-01 15:06 ` Dmitry A. Kazakov
2017-08-01 23:06 ` Randy Brukardt
2017-08-02 6:20 ` Dmitry A. Kazakov
2017-08-03 3:36 ` Randy Brukardt
2017-08-03 7:40 ` Dmitry A. Kazakov
2017-08-04 23:03 ` Randy Brukardt
2017-08-05 8:33 ` Dmitry A. Kazakov
2017-08-07 22:39 ` Randy Brukardt
2017-08-08 6:27 ` Dmitry A. Kazakov
2017-08-09 0:27 ` Randy Brukardt
2017-08-09 7:37 ` Dmitry A. Kazakov
2017-08-09 22:57 ` Randy Brukardt
2017-08-10 7:56 ` Dmitry A. Kazakov
2017-08-11 0:17 ` Randy Brukardt
2017-08-11 6:43 ` Dmitry A. Kazakov
2017-08-11 20:37 ` Randy Brukardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox