comp.lang.ada
 help / color / mirror / Atom feed
* [Newbie] doubly constrained array, dumb question
@ 2018-02-26 16:26 Mehdi Saada
  2018-02-26 17:02 ` J-P. Rosen
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Mehdi Saada @ 2018-02-26 16:26 UTC (permalink / raw)


Hello.

I would like an constrained String subtype with narrower bounds that Positive'Range.
How can I do something like:
subtype Possible_Length is NATURAL range 1..80;
subtype T_Line is String (Possible_Length range <>);
? I know "range <>" isn't included in the definition of "range_constraint". How can I express the same thing.
So that I can get after, a dynamic string with:
A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its range.

Thanks, Mehdi
Mehdi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 16:26 [Newbie] doubly constrained array, dumb question Mehdi Saada
@ 2018-02-26 17:02 ` J-P. Rosen
  2018-02-26 21:40   ` Dmitry A. Kazakov
  2018-02-26 23:26   ` Randy Brukardt
  2018-02-26 20:52 ` Niklas Holsti
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 16+ messages in thread
From: J-P. Rosen @ 2018-02-26 17:02 UTC (permalink / raw)


Le 26/02/2018 à 17:26, Mehdi Saada a écrit :
> Hello.
> 
> I would like an constrained String subtype with narrower bounds that Positive'Range.
> How can I do something like:
> subtype Possible_Length is NATURAL range 1..80;
> subtype T_Line is String (Possible_Length range <>);
> ? I know "range <>" isn't included in the definition of "range_constraint". How can I express the same thing.
> So that I can get after, a dynamic string with:
> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its range.
> 
You can't do that, because a subtype can't be both constrained and
unconstrainde at the same time...

OTOH, you can define your own string type:
type Short_String is array (Possible_length range <>) of character;

Note that this type will still enjoy string literals, and can be
converted back and forth to String.


-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 16:26 [Newbie] doubly constrained array, dumb question Mehdi Saada
  2018-02-26 17:02 ` J-P. Rosen
@ 2018-02-26 20:52 ` Niklas Holsti
  2018-02-27  1:29 ` Mehdi Saada
  2018-02-27  7:38 ` Jacob Sparre Andersen
  3 siblings, 0 replies; 16+ messages in thread
From: Niklas Holsti @ 2018-02-26 20:52 UTC (permalink / raw)


On 18-02-26 18:26 , Mehdi Saada wrote:
> Hello.
>
> I would like an constrained String subtype with narrower bounds that Positive'Range.
> How can I do something like:
> subtype Possible_Length is NATURAL range 1..80;
> subtype T_Line is String (Possible_Length range <>);
> ? I know "range <>" isn't included in the definition of "range_constraint". How can I express the same thing.
> So that I can get after, a dynamic string with:
> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its range.

I think the closest you can come is to define your own string type such 
that it can be type-converted to/from String, for example like this:

    subtype Small_Positive is Positive range 1 .. 20;

    type Short_String is array (Small_Positive range <>) of Character;

    SS : constant Short_String := "abc";

    S : constant String := String (SS);

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 17:02 ` J-P. Rosen
@ 2018-02-26 21:40   ` Dmitry A. Kazakov
  2018-02-26 23:26   ` Randy Brukardt
  1 sibling, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2018-02-26 21:40 UTC (permalink / raw)


On 2018-02-26 18:02, J-P. Rosen wrote:
> Le 26/02/2018 à 17:26, Mehdi Saada a écrit :
>> Hello.
>>
>> I would like an constrained String subtype with narrower bounds that Positive'Range.
>> How can I do something like:
>> subtype Possible_Length is NATURAL range 1..80;
>> subtype T_Line is String (Possible_Length range <>);
>> ? I know "range <>" isn't included in the definition of "range_constraint". How can I express the same thing.
>> So that I can get after, a dynamic string with:
>> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its range.
>>
> You can't do that, because a subtype can't be both constrained and
> unconstrainde at the same time...

Yes, this is quite annoying that composite types cannot be constrained. 
AFAIK, neither of these can be constrained to produce a subtype:

1. array's index
2. array's element
3. record's component
4. access type's target type
5. discriminant
6. argument of a subroutine access type
7. class-wide types (closed range of descendants T'Class (T..S))
8. tagged type primitive operations (disallowing operations)
9. protected and task type entries (disallowing operations)
10. task type entry family

The list is likely incomplete.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 17:02 ` J-P. Rosen
  2018-02-26 21:40   ` Dmitry A. Kazakov
@ 2018-02-26 23:26   ` Randy Brukardt
  2018-02-27  9:01     ` Simon Wright
  1 sibling, 1 reply; 16+ messages in thread
From: Randy Brukardt @ 2018-02-26 23:26 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1562 bytes --]

"J-P. Rosen" <rosen@adalog.fr> wrote in message 
news:p71rvj$vgh$1@gioia.aioe.org...
> Le 26/02/2018 à 17:26, Mehdi Saada a écrit :
>> Hello.
>>
>> I would like an constrained String subtype with narrower bounds that 
>> Positive'Range.
>> How can I do something like:
>> subtype Possible_Length is NATURAL range 1..80;
>> subtype T_Line is String (Possible_Length range <>);
>> ? I know "range <>" isn't included in the definition of 
>> "range_constraint". How can I express the same thing.
>> So that I can get after, a dynamic string with:
>> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its 
>> range.
>>
> You can't do that, because a subtype can't be both constrained and
> unconstrainde at the same time...
>
> OTOH, you can define your own string type:
> type Short_String is array (Possible_length range <>) of character;

Or you could use a subtype with a dynamic predicate:

    subtype Short_String is String
       with Dynamic_Predicate => Short_String'First >= Possible_Length'First 
and Short_String'Last <= Possible_Length'Last;

This would be checked any time that you convert a string value into a 
Short_String subtype (explicitly or implicitly), so it probably would give 
the right effect. Note that unlike a real constraint, it wouldn't have any 
effect on other subtypes, so:
     Silly_Object : Short_String (1..100);
would not raise Constraint_Error, but any attempt to assign into it would 
raise Assertion_Error. (Assuming the Assertion_Policy is Check, it isn't for 
GNAT by default.)

                 Randy.



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 16:26 [Newbie] doubly constrained array, dumb question Mehdi Saada
  2018-02-26 17:02 ` J-P. Rosen
  2018-02-26 20:52 ` Niklas Holsti
@ 2018-02-27  1:29 ` Mehdi Saada
  2018-02-27  9:18   ` Dmitry A. Kazakov
  2018-02-27 14:34   ` Jere
  2018-02-27  7:38 ` Jacob Sparre Andersen
  3 siblings, 2 replies; 16+ messages in thread
From: Mehdi Saada @ 2018-02-27  1:29 UTC (permalink / raw)


> Dmitry said:
> 1. array's index
> 2. array's element
> 3. record's component
> 4. access type's target type
> 5. discriminant
> 6. argument of a subroutine access type
> 7. class-wide types (closed range of descendants T'Class (T..S))
> 8. tagged type primitive operations (disallowing operations)
> 9. protected and task type entries (disallowing operations)
> 10. task type entry family

Oh, I also arrived at that conclusion for the first five + seventh, I didn't imagine the sixth but there's no reason one shouldn't be able to do that.
For 8: a class has any sense at all only if there's no dispatching to an inexistent method, and your forbidding some methods would do the same, raise an exception.
So to avoid that, if you want to disallow operations (without make a dispatching call to it raises an exception), you have to cut the tagged type from its root, by hiding the fact it has a parent type, so as to disinherit the parent's method.
If you don't want that, (I suppose) one would have to implement a per-operation basis dispatching mechanism with a list of allowed types for each methods ?

I don't know enough protected types or task types, but I think you can hide protected operations/entry types as easily, I suppose you can hide the type's progenitors too. Too bad I'm in the middle of writing a line editor (AdaTutor assignement 5), I can't test that.

Could you give an example of 10, the syntax you would like ?

... I wanted to have an anonymous string subtype in a record. Shouldn't have been terribly important. I made without, but had to add a discriminant Length, which is idiotic since the only use for my record type is to be allocated dynamically, and the string field would always be initialized. In that case I wouldn't risk anything by having
type LINK is record
  Next: access LINK;
  TEXT:String;
end record; being legal.
... since all use would be like this: some_access_to_link := new (Next_link, new STRING(TEXT_INPUT)), and TEXT_INPUT would carry its Length information with it and the object would always have a fixed length.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 16:26 [Newbie] doubly constrained array, dumb question Mehdi Saada
                   ` (2 preceding siblings ...)
  2018-02-27  1:29 ` Mehdi Saada
@ 2018-02-27  7:38 ` Jacob Sparre Andersen
  3 siblings, 0 replies; 16+ messages in thread
From: Jacob Sparre Andersen @ 2018-02-27  7:38 UTC (permalink / raw)


Mehdi Saada <00120260a@gmail.com> writes:

> I would like an constrained String subtype with narrower bounds that Positive'Range.
> How can I do something like:
> subtype Possible_Length is NATURAL range 1..80;
> subtype T_Line is String (Possible_Length range <>);
> ? I know "range <>" isn't included in the definition of "range_constraint". How can I express the same thing.
> So that I can get after, a dynamic string with:
> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its range.

You can do it with a dynamic predicate:

   subtype T_Line is String
     with Dynamic_Predicate => T_Line'Length in 1 .. 80;

Greetings,

Jacob
-- 
                      CAUTION
               BLADE EXTREMELY SHARP
                KEEP OUT OF CHILDREN

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-26 23:26   ` Randy Brukardt
@ 2018-02-27  9:01     ` Simon Wright
  2018-02-27 22:11       ` Randy Brukardt
  0 siblings, 1 reply; 16+ messages in thread
From: Simon Wright @ 2018-02-27  9:01 UTC (permalink / raw)


"Randy Brukardt" <randy@rrsoftware.com> writes:

> "J-P. Rosen" <rosen@adalog.fr> wrote in message 
> news:p71rvj$vgh$1@gioia.aioe.org...
>> Le 26/02/2018 à 17:26, Mehdi Saada a écrit :
>>> Hello.
>>>
>>> I would like an constrained String subtype with narrower bounds that 
>>> Positive'Range.
>>> How can I do something like:
>>> subtype Possible_Length is NATURAL range 1..80;
>>> subtype T_Line is String (Possible_Length range <>);
>>> ? I know "range <>" isn't included in the definition of 
>>> "range_constraint". How can I express the same thing.
>>> So that I can get after, a dynamic string with:
>>> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its 
>>> range.
>>>
>> You can't do that, because a subtype can't be both constrained and
>> unconstrainde at the same time...
>>
>> OTOH, you can define your own string type:
>> type Short_String is array (Possible_length range <>) of character;
>
> Or you could use a subtype with a dynamic predicate:
>
>     subtype Short_String is String
>        with Dynamic_Predicate => Short_String'First >= Possible_Length'First 
> and Short_String'Last <= Possible_Length'Last;
>
> This would be checked any time that you convert a string value into a 
> Short_String subtype (explicitly or implicitly), so it probably would give 
> the right effect. Note that unlike a real constraint, it wouldn't have any 
> effect on other subtypes, so:
>      Silly_Object : Short_String (1..100);
> would not raise Constraint_Error, but any attempt to assign into it would 
> raise Assertion_Error. (Assuming the Assertion_Policy is Check, it isn't for 
> GNAT by default.)

I tried this first:

   with Ada.Text_IO;
   procedure Short_String is
      pragma Assertion_Policy (Check);
      subtype Possible_Length is Integer range 0 .. 5;
      subtype Short_String is String
      with Dynamic_Predicate => Short_String'Length <= Possible_Length'Last;
   begin
      for J in 0 .. 6 loop
         declare
            S : Short_String (1 .. J) := (others => 'm');
            --  pragma Assert (S'Length <= Possible_Length'Last);
         begin
            S := (others => 'x');
            Ada.Text_IO.Put_Line (String (S) & S'Length'Img);
         end;
      end loop;
   end Short_String;

and then with your suggestion, and neither raised AE.

I put in an assertion as commented out, and it was triggered.

SPARK detectd the problem ("medium: predicate check might fail").


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27  1:29 ` Mehdi Saada
@ 2018-02-27  9:18   ` Dmitry A. Kazakov
  2018-02-27 11:43     ` Mehdi Saada
  2018-02-27 17:08     ` G. B.
  2018-02-27 14:34   ` Jere
  1 sibling, 2 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2018-02-27  9:18 UTC (permalink / raw)


On 27/02/2018 02:29, Mehdi Saada wrote:

> For 8: a class has any sense at all only if there's no dispatching to an inexistent method, and your forbidding some methods would do the same, raise an exception.

Not in the case of subtypes. For example, the "class" Integer of which 
Positive is a "member" still has negative values, it is just so that you 
cannot tag them Positive. E.g.

    A : Positive := 1;
    B : Positive := 100;
    C : Positive := (A - B) + B; -- This is OK, though A - B = -99

> So to avoid that, if you want to disallow operations (without make a dispatching call to it raises an exception), you have to cut the tagged type from its root, by hiding the fact it has a parent type, so as to disinherit the parent's method.

No. You can convert subtype to its base in order to lift the constraint. 
There is nothing wrong with that.

> If you don't want that, (I suppose) one would have to implement a per-operation basis dispatching mechanism with a list of allowed types for each methods ?

Not necessarily. You simply override disallowed operations with bodies 
raising Constraint_Error. The same technique is used for multiple dispatch:

    type T is tagged ...
    procedure Multiple (X, Y : T); -- This is legal
    type S is new T with ...
    X : T'Class := T;
    Y : T'Class := S;
begin
    Multiple (X, Y); -- Run-time exception!

> Could you give an example of 10, the syntax you would like ?

    type Kind_Of is (Wake_Up, Notify, Warning, Error, Alarm);
    task type Monitor is
       entry Signal (Kind_Of) (No : Positive; Name : String);
    end Monitor;

Now if you wanted to constrain Signal to only Wake_Up..Notify, that 
would not go.

> ... I wanted to have an anonymous string subtype in a record.

Well, that is another issue. Anonymous types (some) are allowed for 
object declarations, but not as part of other types declarations. 
Sometimes it is quite annoying, e.g. when declaring large nested record 
types:

    type Foo is record
       A : record ... end record; -- Illegal
       B : array (...) of ...;    -- Illegal
    end record;

Sometimes one wants anonymous types of arguments:

    procedure Init
              (  Section_1 : record ... end record;
                 Section_2 : record ... end record;
              );

in order to group them. That is illegal too.

> Shouldn't have been terribly important. I made without, but had to add a discriminant Length, which is idiotic since the only use for my record type is to be allocated dynamically, and the string field would always be initialized. In that case I wouldn't risk anything by having
> type LINK is record
>    Next: access LINK;
>    TEXT:String;
> end record; being legal.

This is again a different issue. You need discriminant in order to be 
able to specify/propagate the constraint.

> ... since all use would be like this: some_access_to_link := new (Next_link, new STRING(TEXT_INPUT)), and TEXT_INPUT would carry its Length information with it and the object would always have a fixed length.

No, it can be allocated on the stack and be a component of another type. 
It is a very important mechanism as it allows to avoid implicit use of 
the heap.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27  9:18   ` Dmitry A. Kazakov
@ 2018-02-27 11:43     ` Mehdi Saada
  2018-02-27 14:19       ` Dmitry A. Kazakov
  2018-02-27 17:08     ` G. B.
  1 sibling, 1 reply; 16+ messages in thread
From: Mehdi Saada @ 2018-02-27 11:43 UTC (permalink / raw)


> For example, the "class" Integer of which 
> Positive is a "member" still has negative values, it is just so that you 
> cannot tag them Positive. E.g.
>     A : Positive := 1;
>     B : Positive := 100;
>     C : Positive := (A - B) + B; -- This is OK, though A - B = -99
Interesting, I never tried that ! Very useful indeed.
But those types don't have classes, nor polymorphism. The compiler can't decide at runtime the type of, said, an integer or a real, and determine the constraints allowed and subprograms defined for, with a virtual table. No dynamic binding for untagged types. But I guess you would probably want that !

> Not necessarily. You simply override disallowed operations with bodies 
> raising Constraint_Error. The same technique is used for multiple dispatch:
Yes, I thought so, but it's definitely not my conception of safe and "predictable"... It's safer that "whatever is defined for the parent, won't raise exception for the descendants" holds true at any time.

> > Could you give an example of 10, the syntax you would like ?
>     type Kind_Of is (Wake_Up, Notify, Warning, Error, Alarm);
>     task type Monitor is
>        entry Signal (Kind_Of) (No : Positive; Name : String);
>     end Monitor;
> Now if you wanted to constrain Signal to only Wake_Up..Notify, that 
> would not go.
I see. I can't really imagine a new syntax that would fit with the pre-existing one to do just that since (a far as I get it) true that constraining record/tagged types works by constraining type declarations, not type definitions (components and methods).

> Sometimes it is quite annoying, e.g. when declaring large nested record 
> types:
>     type Foo is record
>        A : record ... end record; -- Illegal
>        B : array (...) of ...;    -- Illegal
>     end record;
I do like anonymous-anything, but isn't a bit overkill here ?
If instanciation takes you as much time or space than definition, I understood you would like to spare you that, but for a record object, instanciation takes one line, definition much more. You would lose in readibily with anonymous record objects for no gain.
> Sometimes one wants anonymous types of arguments:
>     procedure Init
>               (  Section_1 : record ... end record;
Same here. Declare record types or arrays to do the trick. Plus a type specification like that would most unreadable. 

> No, it can be allocated on the stack and be a component of another type. 
> It is a very important mechanism as it allows to avoid implicit use of 
> the heap.
Ah, I knew there was something more behind. Ok !


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27 11:43     ` Mehdi Saada
@ 2018-02-27 14:19       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2018-02-27 14:19 UTC (permalink / raw)


On 27/02/2018 12:43, Mehdi Saada wrote:
>> For example, the "class" Integer of which
>> Positive is a "member" still has negative values, it is just so that you
>> cannot tag them Positive. E.g.
>>      A : Positive := 1;
>>      B : Positive := 100;
>>      C : Positive := (A - B) + B; -- This is OK, though A - B = -99
> Interesting, I never tried that ! Very useful indeed.
> But those types don't have classes, nor polymorphism.

They have "classes", but no *dynamic* polymorphism.

>> Not necessarily. You simply override disallowed operations with bodies
>> raising Constraint_Error. The same technique is used for multiple dispatch:
> Yes, I thought so, but it's definitely not my conception of safe and "predictable"... It's safer that "whatever is defined for the parent, won't raise exception for the descendants" holds true at any time.

It is safe because that is the defined behavior. Disallowed operation is 
defined as exception, not as never-called-thing.

Compare that with zero divide, there is nothing unsafe in that, it is 
defined to raise Constraint_Error. Unsafe is when a contract is 
violated. If a client promises not to propagate exceptions and then 
divides to zero without catching Constraint_Error, that is an unsafe 
*client*, division is still safe.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27  1:29 ` Mehdi Saada
  2018-02-27  9:18   ` Dmitry A. Kazakov
@ 2018-02-27 14:34   ` Jere
  2018-02-27 15:13     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 16+ messages in thread
From: Jere @ 2018-02-27 14:34 UTC (permalink / raw)


On Monday, February 26, 2018 at 8:29:17 PM UTC-5, Mehdi Saada wrote:
> > Dmitry said:
> > 1. array's index
> > 2. array's element
> > 3. record's component
> > 4. access type's target type
> > 5. discriminant
> > 6. argument of a subroutine access type
> > 7. class-wide types (closed range of descendants T'Class (T..S))
> > 8. tagged type primitive operations (disallowing operations)
> > 9. protected and task type entries (disallowing operations)
> > 10. task type entry family
> 
> For 8: a class has any sense at all only if there's no dispatching to an inexistent method, and your forbidding some methods would do the same, raise an exception.
> So to avoid that, if you want to disallow operations (without make a dispatching call to it raises an exception), you have to cut the tagged type from its root, by hiding the fact it has a parent type, so as to disinherit the parent's method.
> If you don't want that, (I suppose) one would have to implement a per-operation basis dispatching mechanism with a list of allowed types for each methods ?
> 

Two other options:
1.  Full composition with switch statements (which is akin to that last
    sentence I quoted).
2.  The Rust inheritance model, which is a bit more complex.

I've been dorking around with Rust some and while I don't like all the
language has to offer, it's inheritance model is intriguing.  Basically,
you implement type extension purely through composition.  Normally, this
would force you to use switch statements, etc to emulate dispatching, but
in Rust, they allow you to implement interfaces (or traits as Rust calls
them).  So the design idea is anything that you actually want to dispatch
on is relegated to an interface (or multiple interfaces), and you use
classwide types of the interface to do the dispatching.  This makes for
what is practically a max 1 level deep extension tree.  Composition
allows for you to hide non dispatching operations as you see fit (I.E.
you only implement the ones you want your client to see) while the
interfaces allow you to cleanly dispatch for operations.  You are also
free to only implement the interfaces you want for each level of
inheritance.  So parent classes can implement interfaces that child
classes do not.

I'm not saying this is the end all be all method, but it is definitely
interesting to tinker with and makes you consider designs a bit
differently (much like pure composition does).

Example:
*********************************************************************

with Ada.Text_IO; use Ada.Text_IO;

procedure jdoodle is

   ------------------------------------------------------------------------
   --  This provides the interface for some dispatchable process
   --  along with a default implementation (optional).
   --
   package Dispatching is
      type Dispatchable1 is limited interface;
      procedure Do_Something(Self : Dispatchable1) is abstract;
      
      type Default_Dispatchable1 is new Dispatchable1 with null record;
      overriding procedure Do_Something(Self : Default_Dispatchable1);
      
      type Dispatchable2 is limited interface;
      procedure Do_Another(Self : Dispatchable2) is abstract;
   end Dispatching;
   
   package body Dispatching is
      procedure Do_Something(Self : Default_Dispatchable1) is
      begin
         Put_Line("Default Dispatching");
      end Do_Something;
   end Dispatching;

   ------------------------------------------------------------------------
   --  This is the base class which implements some dispatchable process
   --
   use Dispatching;
   package Base is
      type Instance is new Dispatchable1 and Dispatchable2 with private;
      overriding procedure Do_Something(Self : Instance) with Inline;
      overriding procedure Do_Another(Self : Instance);
      not overriding procedure Other_Op(Self : Instance);
   private
      type Instance is new Dispatchable1 and Dispatchable2 with record
         Parent : Default_Dispatchable1;  -- Composition
      end record;
   end Base;
   
   package body Base is
      procedure Do_Something(Self : Instance) is
      begin
         Put("Base Dispatching => ");
         Self.Parent.Do_Something;
      end Do_Something;
      
      procedure Do_Another(Self : Instance) is
      begin
         Put_Line("Another Dispatching");
      end Do_Another;
      
      procedure Other_Op(Self : Instance) is
      begin
         Put_Line("Some other op");
      end Other_Op;
   end Base;
   
   ------------------------------------------------------------------------
   --  This is a derived class which implements some dispatchable process
   --  Inheritance is done through composition.  Note that it does
   --  not proved Other_Op
   --
   package Derived is
      type Instance is new Dispatchable1 with private;
      overriding procedure Do_Something(Self : Instance) with Inline;
   private
      type Instance is new Dispatchable1 with record
         Parent : Base.Instance;  -- Composition
      end record;
   end Derived;
   
   package body Derived is
      procedure Do_Something(Self : Instance) is
      begin
         Put("Derived Dispatching => ");
         Self.Parent.Do_Something;
      end Do_Something;
   end Derived;
   
   
   ------------------------------------------------------------------------
   --  Some local variables to test with
   --
   v1 : Base.Instance;
   v2 : Derived.Instance;
   
   -----------------------------------------------------------------------
   --  Dispatching calls all handled through interface classwide types
   --
   procedure Some_Function_Call(Object : Dispatchable1'Class) is
   begin
      Object.Do_Something;
   end Some_Function_Call;
   
   procedure Some_Other_Call(Object : Dispatchable2'Class) is
   begin
      Object.Do_Another;
   end Some_Other_Call;
   
begin
   Put_Line("Starting");
   Some_Function_Call(v1);
   Some_Function_Call(v2);
   
   Some_Other_Call(v1);
   --Some_Other_Call(v2);  -- would raise a compiler error
end jdoodle;


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27 14:34   ` Jere
@ 2018-02-27 15:13     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2018-02-27 15:13 UTC (permalink / raw)


On 27/02/2018 15:34, Jere wrote:
> So the design idea is anything that you actually want to dispatch
> on is relegated to an interface (or multiple interfaces), and you use
> classwide types of the interface to do the dispatching.

I do that in Ada quite customary by declaring an interface before most 
of concrete types.

> This makes for
> what is practically a max 1 level deep extension tree.

Not really. It is not a directly related thing.

> Composition
> allows for you to hide non dispatching operations as you see fit (I.E.
> you only implement the ones you want your client to see) while the
> interfaces allow you to cleanly dispatch for operations.  You are also
> free to only implement the interfaces you want for each level of
> inheritance.  So parent classes can implement interfaces that child
> classes do not.

This is delegation I wanted Ada to have for a long time. Thus would be a 
way to work around limitations put upon multiple inheritance. Combined 
with declaring interfaces it could be a quite powerful model:

    type Interface_T is interface;
    procedure Foo (X : Interface_T) is abstract;

    type T is new Interface_T ...;
    overriding procedure Foo (X : T);

    type S is new Controlled and Interface_T ... with record
       X : S; -- ARG won't let me derive from T, I do it anyway
    end record;
    overriding procedure Foo (X : S) is delegated S.X;

Even more flexible if Ada allowed getting interfaces from concrete 
types. E.g. instead of

    type Interface_T is interface;
    procedure Foo (X : Interface_T) is abstract;

    type T is new Interface_T ...;
    overriding procedure Foo (X : T);

You do

    type T is tagged ...;
    procedure Foo (X : T);

and use T'Interface instead of explicit declaration of Interface_T.

> I'm not saying this is the end all be all method, but it is definitely
> interesting to tinker with and makes you consider designs a bit
> differently (much like pure composition does).

Everything must be a method = there must be a class for any 
type-operation pair.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27  9:18   ` Dmitry A. Kazakov
  2018-02-27 11:43     ` Mehdi Saada
@ 2018-02-27 17:08     ` G. B.
  2018-02-27 17:37       ` Dmitry A. Kazakov
  1 sibling, 1 reply; 16+ messages in thread
From: G. B. @ 2018-02-27 17:08 UTC (permalink / raw)


Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
> On 27/02/2018 02:29, Mehdi Saada wrote:
> 
>> For 8: a class has any sense at all only if there's no dispatching to an
>> inexistent method, and your forbidding some methods would do the same, raise an exception.
> 
> Not in the case of subtypes. For example, the "class" Integer of which 
> Positive is a "member"

Or a subset, rather? All x in Positive are such that
x in Integer, but Positive is not an element of Integer.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27 17:08     ` G. B.
@ 2018-02-27 17:37       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry A. Kazakov @ 2018-02-27 17:37 UTC (permalink / raw)


On 2018-02-27 18:08, G. B. wrote:
> Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote:
>> On 27/02/2018 02:29, Mehdi Saada wrote:
>>
>>> For 8: a class has any sense at all only if there's no dispatching to an
>>> inexistent method, and your forbidding some methods would do the same, raise an exception.
>>
>> Not in the case of subtypes. For example, the "class" Integer of which
>> Positive is a "member"
> 
> Or a subset, rather? All x in Positive are such that
> x in Integer, but Positive is not an element of Integer.

Subsetting is an "implementation" to subclassing. The latter also 
inherits operations as Positive does. Subsetting is silent about operations.

Note that even unary minus is inherited and kind of works, because 
Constraint_Error is a part of the contract. That is a way around LSP ...

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Newbie] doubly constrained array, dumb question
  2018-02-27  9:01     ` Simon Wright
@ 2018-02-27 22:11       ` Randy Brukardt
  0 siblings, 0 replies; 16+ messages in thread
From: Randy Brukardt @ 2018-02-27 22:11 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 3719 bytes --]

"Simon Wright" <simon@pushface.org> wrote in message 
news:lyefl6kdrc.fsf@pushface.org...
> "Randy Brukardt" <randy@rrsoftware.com> writes:
>
>> "J-P. Rosen" <rosen@adalog.fr> wrote in message
>> news:p71rvj$vgh$1@gioia.aioe.org...
>>> Le 26/02/2018 à 17:26, Mehdi Saada a écrit :
>>>> Hello.
>>>>
>>>> I would like an constrained String subtype with narrower bounds that
>>>> Positive'Range.
>>>> How can I do something like:
>>>> subtype Possible_Length is NATURAL range 1..80;
>>>> subtype T_Line is String (Possible_Length range <>);
>>>> ? I know "range <>" isn't included in the definition of
>>>> "range_constraint". How can I express the same thing.
>>>> So that I can get after, a dynamic string with:
>>>> A: access T_LINE := new T_LINE'("BLABLABLA"); while checking for its
>>>> range.
>>>>
>>> You can't do that, because a subtype can't be both constrained and
>>> unconstrainde at the same time...
>>>
>>> OTOH, you can define your own string type:
>>> type Short_String is array (Possible_length range <>) of character;
>>
>> Or you could use a subtype with a dynamic predicate:
>>
>>     subtype Short_String is String
>>        with Dynamic_Predicate => Short_String'First >= 
>> Possible_Length'First
>> and Short_String'Last <= Possible_Length'Last;
>>
>> This would be checked any time that you convert a string value into a
>> Short_String subtype (explicitly or implicitly), so it probably would 
>> give
>> the right effect. Note that unlike a real constraint, it wouldn't have 
>> any
>> effect on other subtypes, so:
>>      Silly_Object : Short_String (1..100);
>> would not raise Constraint_Error, but any attempt to assign into it would
>> raise Assertion_Error. (Assuming the Assertion_Policy is Check, it isn't 
>> for
>> GNAT by default.)
>
> I tried this first:
>
>   with Ada.Text_IO;
>   procedure Short_String is
>      pragma Assertion_Policy (Check);
>      subtype Possible_Length is Integer range 0 .. 5;
>      subtype Short_String is String
>      with Dynamic_Predicate => Short_String'Length <= 
> Possible_Length'Last;
>   begin
>      for J in 0 .. 6 loop
>         declare
>            S : Short_String (1 .. J) := (others => 'm');

Argubly, assigning an aggregate doesn't involve a subtype conversion, in 
which case there would be no predicate check. You could also argue the 
reverse: 5.2(11) says that there is a conversion. A compiler would be 
allowed to omit it if it can prove that it already passed -- but not to 
ignore it.

>            --  pragma Assert (S'Length <= Possible_Length'Last);
>         begin
>            S := (others => 'x');
>            Ada.Text_IO.Put_Line (String (S) & S'Length'Img);
>         end;
>      end loop;
>   end Short_String;
>
> and then with your suggestion, and neither raised AE.
>
> I put in an assertion as commented out, and it was triggered.
>
> SPARK detectd the problem ("medium: predicate check might fail").

I think it is is GNAT bug, but it is very much on the edge -- as noted 
above, an assignment should check the predicate, but we also want compilers 
to be able to eliminate redundant checks. In this case, it appears that no 
one is checking the predicate, which hardly makes the assignment check 
redundant (thus it is a bug). (That is, it would be OK if the aggregate 
checked the predicate, but it doesn't actually have a reason to do so -- so 
the assignment ought to be doing it. For other subtype checks on "others" 
aggregates, the aggregate itself has to make them. That might cause the 
compiler to not make the subtype check on the assignment, thinking it is 
redundant [but it isn't if the subtype has a predicate]).

                             Randy.


                                  Randy.



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2018-02-27 22:11 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-26 16:26 [Newbie] doubly constrained array, dumb question Mehdi Saada
2018-02-26 17:02 ` J-P. Rosen
2018-02-26 21:40   ` Dmitry A. Kazakov
2018-02-26 23:26   ` Randy Brukardt
2018-02-27  9:01     ` Simon Wright
2018-02-27 22:11       ` Randy Brukardt
2018-02-26 20:52 ` Niklas Holsti
2018-02-27  1:29 ` Mehdi Saada
2018-02-27  9:18   ` Dmitry A. Kazakov
2018-02-27 11:43     ` Mehdi Saada
2018-02-27 14:19       ` Dmitry A. Kazakov
2018-02-27 17:08     ` G. B.
2018-02-27 17:37       ` Dmitry A. Kazakov
2018-02-27 14:34   ` Jere
2018-02-27 15:13     ` Dmitry A. Kazakov
2018-02-27  7:38 ` Jacob Sparre Andersen

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