Ada: How to Iterate over a private map? - containers

Consider the following:
with Ada.Containers.Hashed_Maps;
with Ada.Containers; use Ada.Containers;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
package Tiles is
-- Implementation is completely hidden
type Tile_Type is private;
type Tile_Set is tagged private;
type Tile_Key is private;
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type);
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type;
function Make (Key : Natural; Data : Integer) return Tile_Type;
function Image (Tile : Tile_Type) return String;
private
type Tile_Key is record
X : Natural;
end record;
function Tile_Hash (K : Tile_Key) return Hash_Type is
(Hash_Type (K.X));
type Tile_Type is record
Key : Tile_Key;
Data : Integer;
end record;
package Tile_Matrix is new Ada.Containers.Hashed_Maps
(Element_Type => Tile_Type,
Key_Type => Tile_Key,
Hash => Tile_Hash,
Equivalent_Keys => "=");
use Tile_Matrix;
type Tile_Set is new Tile_Matrix.Map with null record;
end Tiles;
package body Tiles is
procedure Add (Collection : in out Tile_Set; Tile : Tile_Type) is
begin
Collection.Include (Key => Tile.Key, New_Item => Tile);
end Add;
function Get (Collection : in Tile_Set; Key : Natural) return Tile_Type is
K : Tile_Key := (X => Key);
C : Cursor := Collection.Find (Key => K);
begin -- For illustration, would need to handle missing keys
return Result : Tile_Type do
Result := Collection (C);
end return;
end Get;
function Image (Tile : Tile_Type) return String is
(Tile.Key.X'Image & '=' & Tile.Data'Image);
function Make (Key : Natural; Data : Integer) return Tile_Type is
New_Key : Tile_Key := (X => Key);
begin
return Result : Tile_Type do
Result.Key := New_Key;
Result.Data := Data;
end return;
end Make;
end Tiles;
use Tiles;
S : Tile_Set;
T : Tile_Type;
begin
S.Add (Make (Key => 1, Data => 10));
T := S.Get (1);
Put_Line (Image (T)); -- 1, 10
S.Add (Make (Key => 2, Data => 20));
T := S.Get (2);
Put_Line (Image (T)); -- 1, 20
for X in S loop -- Fails: cannot iterate over "Tile_Set"
-- +: to iterate directly over the elements of a container, write "of S"
-- but "for X of S" doesn't work either.
T := S (X); -- Fails: array type required in indexed component
-- presumably because X isn't a cursor?
Put_Line (Image (T));
end loop;
end;
It seems to me that the compiler has enough knowledge to iterate over a Tile_Set and I'm supposing it won't because I haven't exposed an iterator.
How should I modify this so that 'for X is S loop' is valid?
More generally, what is the idiom for hiding the implementation of underlying containers, whilst exposing indexing, iterating etc.?

It seems to me that the compiler has enough knowledge to iterate over a Tile_Set and I'm supposing it won't because I haven't exposed an iterator.
That assessment is correct. To be able to loop over a type, the type needs to define the aspects Default_Iterator and Iterator_Element, as described in LRM 5.5.1, and the aspect Constant_Indexing as described in LRM 4.1.6. Both sections read
These aspects are inherited by descendants of type T (including T'Class).
This means that since Tile_Set inherits from Tile_Matrix.Map, it does inherit these aspects which are defined on that map. However, since the inheritance relation is private, the aspects are not visible outside of that package.
You also cannot set them for the private type explicitly since 4.1.6 says
The aspects shall not be overridden, but the functions they denote may be.
Setting them on the private type would override the aspects inherited in the private part.
That leaves you with two options:
Make the inheritance relation public so that you get immediate access to all of the aspects.
Make Tile_Set encapsulate the Hashed_Map value, so that you can implement your own iteration on the type.
The second option would look like this:
type Cursor is private;
type Tile_Set is private
with Default_Iterator => Iterate,
Iterator_Element => Tile_Type,
Constant_Indexing => Constant_Reference;
function Has_Element (Position: Cursor) return Boolean;
package Tile_Set_Iterator_Interfaces is new
Ada.Iterator_Interfaces (Cursor, Has_Element);
type Constant_Reference_Type
(Element : not null access constant Tile_Type) is private
with Implicit_Dereference => Element;
function Iterate (Container: in Tile_Set) return
Tile_Set_Iterator_Interfaces.Forward_Iterator'Class;
function Constant_Reference (Container : aliased in Tile_Set;
Position : Cursor)
return Constant_Reference_Type;
private
-- ..
type Cursor is record
Data : Tile_Matrix.Cursor;
end record;
type Tile_Set is record
Data : Tile_Matrix.Map;
end record;
In the implementation of these subroutines, you can simply delegate to the Tile_Matrix subroutines.
The lesson is that you shouldn't inherit when your actual intent is composition.

Related

Parsing json5/js object literals in Ada

New to Ada. Trying to work with some objects like the following {name:'Hermann',age:33} in an a project and I'd rather not write my own json parser for this. Is there either:
a way for me to configure Gnatcolls.JSON to parse and write these objects
or
a different library I can use with json5 or javascript object literal support?
I wrote a JSON parser from the spec pretty quickly for work while doing other things, and it took a day/day-and-a-half; it's not particularly hard and I'll see about posting it to github or something.
However, JSON5 is different enough that re-implementing it would be on the order of the same difficulty as writing some sort of adaptor. Editing the parser to accept the new constructs might be more difficult than one might anticipate, as the IdentifierName allowed as a key means that you can't simply chain together the sequence (1) "get-open-brace", (2) "consume-whitespace", (3) "get-a-string", (4) "consume-whitespace", (5) "get-a-colon, (6) "consume-whitespace", (7) "get-JSON-object", (8) "consume-whitespace", (9) "get-a-character; if comma, go to #1, otherwise it should be an end-brace".
Perhaps one thing that makes things easier is to equate the stream- and string-operations so that you only have one production-method for your objects; there are three main ways to do this:
Make a generic such that it takes a string and gives the profile for the stream-operation.
Make a pair of overloaded functions that provide the same interface.
Make a stream that is a string; the following does this:
Package Example is
-- String_Stream allows uses a string to buffer the underlying stream,
-- it may be initialized with content from a string or a given length for
-- the string underlying the stream.
--
-- This is intended for the construction and consumption of string-data
-- using stream-operations
Type String_Stream(<>) is new Ada.Streams.Root_Stream_Type with Private;
Subtype Root_Stream_Class is Ada.Streams.Root_Stream_Type'Class;
-- Create a String_Stream.
Function "+"( Length : Natural ) return String_Stream;
Function "+"( Text : String ) return String_Stream;
Function "+"( Length : Natural ) return not null access Root_Stream_Class;
Function "+"( Text : String ) return not null access Root_Stream_Class;
-- Retrieve the remaining string-data; the (POSITION..DATA'LENGTH) slice.
Function "-"( Stream : String_Stream ) return String;
-- Retrieve the string-data; the (1..DATA'LENGTH) slice.
Function Data(Stream : String_Stream ) return String;
Private
Pragma Assert( Ada.Streams.Stream_Element'Size = String'Component_Size );
Overriding
procedure Read
(Stream : in out String_Stream;
Item : out Ada.Streams.Stream_Element_Array;
Last : out Ada.Streams.Stream_Element_Offset);
Overriding
procedure Write
(Stream : in out String_Stream;
Item : Ada.Streams.Stream_Element_Array);
Type String_Stream(Length : Ada.Streams.Stream_Element_Count) is
new Ada.Streams.Root_Stream_Type with record
Data : Ada.Streams.Stream_Element_Array(1..Length);
Position : Ada.Streams.Stream_Element_Count;
End record;
End Example;
With implementation of:
Package Body Example is
Use Ada.Streams;
-------------------
-- INITALIZERS --
-------------------
Function From_String( Text : String ) return String_Stream
with Inline, Pure_Function;
Function Buffer ( Length : Natural ) return String_Stream
with Inline, Pure_Function;
--------------
-- R E A D --
--------------
Procedure Read
(Stream : in out String_Stream;
Item : out Ada.Streams.Stream_Element_Array;
Last : out Ada.Streams.Stream_Element_Offset) is
Use Ada.IO_Exceptions, Ada.Streams;
Begin
-- When there is a read of zero, do nothing.
-- When there is a read beyond the buffer's bounds, raise an exception.
-- Note: I've used two cases here-
-- 1) when the read is greater than the buffer,
-- 2) when the read would go beyond the buffer.
-- Finally, read the given amount of data and update the position.
if Item'Length = 0 then
null;
elsif Item'Length > Stream.Data'Length then
Raise End_Error with "Request is larger than the buffer's size.";
elsif Stream_Element_Offset'Pred(Stream.Position)+Item'Length > Stream.Data'Length then
Raise End_Error with "Buffer will over-read.";
else
Declare
Subtype Selection is Stream_Element_Offset range
Stream.Position..Stream.Position+Stream_Element_Offset'Pred(Item'Length);
Begin
Item(Item'Range):= Stream.Data(Selection);
Stream.Position:= Stream_Element_Offset'Succ(Selection'Last);
Last:= Selection'Last;--Stream.Position;
End;
end if;
End Read;
-----------------
-- W R I T E --
-----------------
Procedure Write
(Stream : in out String_Stream;
Item : Ada.Streams.Stream_Element_Array) is
Begin
Declare
Subtype Selection is Stream_Element_Offset range
Stream.Position..Stream.Position+Stream_Element_Offset'Pred(Item'Length);
Begin
Stream.Data(Selection):= Item(Item'Range);
Stream.Position:= Stream_Element_Offset'Succ(Selection'Last);
End;
End Write;
----------------------------------
-- INITALIZER IMPLEMENTATIONS --
----------------------------------
-- Create a buffer of the given length, zero-filled.
Function Buffer( Length : Natural ) return String_Stream is
Len : Constant Ada.Streams.Stream_Element_Offset :=
Ada.Streams.Stream_Element_Offset(Length);
Begin
Return Result : Constant String_Stream:=
(Root_Stream_Type with
Position => 1,
Data => (1..Len => 0),
Length => Len
);
End Buffer;
-- Create a buffer from the given string.
Function From_String( Text : String ) return String_Stream is
Use Ada.Streams;
Subtype Element_Range is Stream_Element_Offset range
Stream_Element_Offset(Text'First)..Stream_Element_Offset(Text'Last);
Subtype Constrained_Array is Stream_Element_Array(Element_Range);
Subtype Constrained_String is String(Text'Range);
Function Convert is new Ada.Unchecked_Conversion(
Source => Constrained_String,
Target => Constrained_Array
);
Begin
Return Result : Constant String_Stream:=
(Root_Stream_Type with
Position => Element_Range'First,
Data => Convert( Text ),
Length => Text'Length
);
End From_String;
-- Classwide returning renames, for consistancy/overload.
Function To_Stream( Text : String ) return Root_Stream_Class is
( From_String(Text) ) with Inline, Pure_Function;
Function To_Stream( Length : Natural ) return Root_Stream_Class is
( Buffer(Length) ) with Inline, Pure_Function;
----------------------------
-- CONVERSION OPERATORS --
----------------------------
-- Allocating / access-returning initalizing operations.
Function "+"( Length : Natural ) return not null access Root_Stream_Class is
( New Root_Stream_Class'(To_Stream(Length)) );
Function "+"( Text : String ) return not null access Root_Stream_Class is
( New Root_Stream_Class'(To_Stream(Text)) );
-- Conversion from text or integer to a stream; renaming of the initalizers.
Function "+"( Text : String ) return String_Stream renames From_String;
Function "+"( Length : Natural ) return String_Stream renames Buffer;
-- Convert a given Stream_Element_Array to a String.
Function "-"( Data : Ada.Streams.Stream_Element_Array ) Return String is
Subtype Element_Range is Natural range
Natural(Data'First)..Natural(Data'Last);
Subtype Constrained_Array is Stream_Element_Array(Data'Range);
Subtype Constrained_String is String(Element_Range);
Function Convert is new Ada.Unchecked_Conversion(
Source => Constrained_Array,
Target => Constrained_String
);
Begin
Return Convert( Data );
End "-";
----------------------
-- DATA RETRIEVAL --
----------------------
Function "-"( Stream : String_Stream ) return String is
Begin
Return -Stream.Data(Stream.Position..Stream.Length);
End "-";
Function Data(Stream : String_Stream ) return String is
Begin
Return -Stream.Data;
End Data;
End Example;

Returning a function in Ada

Is it possible, that a function can return a function in Ada? I am trying to get currying to work.
type Integer_Func_Type is access function (Y : Integer) return Integer;
function Add (X : Integer) return Integer_Func_Type is
function Inner (Y : Integer) return Integer is
begin
return X + Y;
end Inner;
begin
return Inner'Access;
end;
At the end, I do not want to provide all arguments of a function one at a time. For example: if x is a ternary function and y is curry(x), then I can use following function calls: y(a,b,c), y(a,b)(c), y(a)(b,c), y(a)(b)(c).
EDIT
I implemented 'Jacob Sparre Andersen' suggestions. But it does not look like currying will be easy to implement. I must implement every possible variant of any type I want to use in advance. Is this correct?
with Ada.Text_IO;
with R;
procedure Hello is
Add_Two : R.Test2 := (X => 2);
begin
Ada.Text_IO.Put_Line(Add_Two.Add(3)'Img);
end Hello;
r.adb
package body R is
function Add(A : Test2; Y : Integer) return Integer is
begin
return A.X + Y;
end Add;
end R;
r.ads
package R is
type Test is abstract tagged null record;
function Add(A : Test; Y : Integer) return Integer is abstract;
type Test2 is new Test with
record
X : Integer;
end record;
overriding
function Add(A : Test2; Y : Integer) return Integer;
end R;
This is how to do it with generics:
with Ada.Text_IO;
procedure Test is
-- shorthand Ada 2012 syntax; can also use full body
function Add (X, Y : Integer) return Integer is (X + Y);
generic
type A_Type (<>) is limited private;
type B_Type (<>) is limited private;
type Return_Type (<>) is limited private;
with function Orig (A : A_Type; B : B_Type) return Return_Type;
A : A_Type;
function Curry_2_to_1 (B : B_Type) return Return_Type;
function Curry_2_to_1 (B : B_Type) return Return_Type is
(Orig (A, B));
function Curried_Add is new Curry_2_to_1
(Integer, Integer, Integer, Add, 3);
begin
Ada.Text_IO.Put_Line (Integer'Image (Curried_Add (39)));
end Test;
As you see, it is quite verbose. Also, you need to provide a currying implementation for every count X of parameters of the original function and every number Y of parameters of the generated function, so you'd have a lot of Curry_X_to_Y functions. This is necessary because Ada does not have variadic generics.
A lot of the verbosity also comes from Ada not doing type inference: You need to explicitly specifiy A_Type, B_Type and Return_Type even though theoretically, they could be inferred from the given original function (this is what some functional programming languages do).
Finally, you need a named instance from the currying function because Ada does not support anonymous instances of generic functions.
So, in principle, currying does work, but it is not anything as elegant as in a language like Haskell. If you only want currying for a specific type, the code gets significantly shorter, but you also lose flexibility.
You can't do quite what you're trying to do, since Inner stops to exist as soon as Add returns.
You could do something with the effect you describe using tagged types.
One abstract tagged type with a primitive operation matching your function type.
And then a derived tagged type with X as an attribute and an implementation of the function matching Inner.
Many of the answers seem to deal with ways to have subprograms that deal with variable numbers of parameters. One way to deal with this is with a sequence of values. For example,
type Integer_List is array (Positive range <>) of Integer;
function Add (List : Integer_List) return Integer;
can be considered a function that takes an arbitrary number of parameters of type Integer. This is simple if all your parameters have the same type. It's more complicated, but still possible, if you deal with a finite set of possible parameter types:
type Number_ID is (Int, Flt, Dur);
type Number (ID : Number_ID) is record
case ID is
when Int =>
Int_Value : Integer;
when Flt =>
Flt_Value : Float;
when Dur =>
Dur_Value : Duration;
end case;
end record;
type Number_List is array (Positive range <>) of Number;
function Add (List : Number_List) return Number;
If you have to be able to handle types not known in advance, this technique is not suitable.

In Ada, How do I recursively map and memory manage a type within itself

I've been struggling with this little issue for a while. I am trying to create my own implementation of an internal JSON structure. The challenge is that with Ada I have to use an access type to make it recursive and access types have the risk of leaking if I don't have it tightly controlled. In order to make it controlled, I kept all the real activity private I provided Get (Source:...) and Set (Target:...; Value:...) functions/procedures for the Node type that will attempt to verify and handle any existing Vector (json-array) or Map (json-object) elements. In order to further ensure that I was using stable features of Ada 2012 and catching contents as they go out of scope, I tried to use a Protected_Controlled type and "managing" Ada libraries, but found that the container libraries couldn't handle protected types, so I used simply Controlled. The Finalize (...) procedure is for any Vector or Map types and recursively frees the Node_Value.Reference.
My question is if I am applying Ada 2012 correctly, or else how do I create a memory managed recursion of a type that could be either a vector/map or a string/number?
private
...
type Node_Access is access Node;
type Node_Value is new Ada.Finalization.Controlled with record
Reference : Node_Access;
end record;
overriding procedure Initialize (Item : in out Node_Value);
overriding procedure Adjust (Item : in out Node_Value);
overriding procedure Finalize (Item : in out Node_Value);
...
package Of_Array is new Ada.Containers.Indefinite_Vectors (Natural, Node_Value);
package Of_Object is new Ada.Containers.Indefinite_Ordered_Maps (Wide_String, Node_Value);
type Node is record
...
Vector : aliased Of_Array.Vector;
Object : aliased Of_Object.Map;
end record
with Size => 96;
procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Access);
The way to do it (in my opinion) is to use OOP and have an abstract element as the root node of a family of types representing the different kinds of data which can be stored.
An array of elements can then be implemented as a vector of the class rooted at the abstract element type. An "object" can be implemented as a hash-table with a string key and the class rooted at the abstract element type as the values.
Self-referential types without access types are a valid use for type extension in combination with an indefinite container. A simple example is S-expressions, or Sexes. A Sex is either an atom or a list of zero or more Sexes. The right way to be able to do this would be
with Ada.Containers.Indefinite_Vectors;
package Sexes is
type Sex is private;
-- Operations on Sex
private -- Sexes
package Sex_List is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive, Element_Type => Sex); -- Illegal
type Sex (Is_Atom : Boolean := False) is record
case Is_Atom is
when False =>
Value : Atom;
when True =>
List : Sex_List.Vector;
end case;
end record;
end Sexes;
but Ada doesn't allow this. We can use type extension to get around this:
private -- Sexes
type Root is tagged null record;
package Sex_List is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive, Element_Type => Root'Class);
type Sex (Is_Atom : Boolean := False) is new Root with record
case Is_Atom is
when False =>
Value : Atom;
when True =>
List : Sex_List.Vector;
end case;
end record;
end Sexes;
which is legal. The only catch is that you have to convert anything taken from List to Sex (or Node in your case).
HTH; sorry about the late response.

Why is my Scala function returning type Unit and not whatever is the last line?

I am trying to figure out the issue, and tried different styles that I have read on Scala, but none of them work. My code is:
....
val str = "(and x y)";
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int )
var b = pos; //position of where in the expression String I am currently in
val temp = expreshHolder; //holder of expressions without parens
var arrayCounter = follow; //just counts to make sure an empty spot in the array is there to put in the strings
if(exp(b) == '(') {
b = b + 1;
while(exp(b) == ' '){b = b + 1} //point of this is to just skip any spaces between paren and start of expression type
if(exp(b) == 'a') {
temp(arrayCounter) = exp(b).toString;
b = b+1;
temp(arrayCounter)+exp(b).toString; b = b+1;
temp(arrayCounter) + exp(b).toString; arrayCounter+=1}
temp;
}
}
val hold: ArrayBuffer[String] = stringParse(str, 0, new ArrayBuffer[String], 0);
for(test <- hold) println(test);
My error is:
Driver.scala:35: error: type mismatch;
found : Unit
required: scala.collection.mutable.ArrayBuffer[String]
ho = stringParse(str, 0, ho, 0);
^one error found
When I add an equals sign after the arguments in the method declaration, like so:
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int ) ={....}
It changes it to "Any". I am confused on how this works. Any ideas? Much appreciated.
Here's a more general answer on how one may approach such problems:
It happens sometimes that you write a function and in your head assume it returns type X, but somewhere down the road the compiler disagrees. This almost always happens when the function has just been written, so while the compiler doesn't give you the actual source (it points to the line where your function is called instead) you normally know that your function's return type is the problem.
If you do not see the type problem straight away, there is the simple trick to explicitly type your function. For example, if you thought your function should have returned Int, but somehow the compiler says it found a Unit, it helps to add : Int to your function. This way, you help the compiler to help you, as it will spot the exact place, where a path in your function returns a non-Int value, which is the actual problem you were looking for in the first place.
You have to add the equals sign if you want to return a value. Now, the reason that your function's return value is Any is that you have 2 control paths, each returning a value of a different type - 1 is when the if's condition is met (and the return value will be temp) and the other is when if's condition isn't (and the return value will be b=b+1, or b after it's incremented).
class Test(condition: Boolean) {
def mixed = condition match {
case true => "Hi"
case false => 100
}
def same = condition match {
case true => List(1,2,3)
case false => List(4,5,6)
}
case class Foo(x: Int)
case class Bar(x: Int)
def parent = condition match {
case true => Foo(1)
case false => Bar(1)
}
}
val test = new Test(true)
test.mixed // type: Any
test.same // type List[Int]
test.parent // type is Product, the case class super type
The compiler will do its best to apply the most specific type it can based on the possible set of result types returned from the conditional (match, if/else, fold, etc.).

Ada protected types

I've got a simple package in Ada with procedures and functions. I'd like to have all the functions and procedures in a protected type.
e.g. for a simple .adb file
package body Pack is
procedure procedure1 (B : in out Integer) is
begin
B := new Integer;
end procedure1;
procedure procedure2 (B: in out Integer) is
begin
B.Cont(B.First-1) := 1;
end procedure2;
function procedure3 (B : Integer) return Boolean is
begin
return B.First = B.Last;
end procedure3;
end pack;
and or a simple .ads
package body Pack is
procedure procedure1 (B : in out Integer);
procedure procedure2 (B: in out Integer);
function procedure3 (B : Integer) return Boolean;
end pack;
How would I go about it?
The thing about a protected type is that it protects something (against concurrent access). It’s hard to see from your code what it is you want to protect.
If, say, you wanted to do a thread-safe increment, you might have a spec like
package Pack is
protected type T is
procedure Set (To : Integer);
procedure Increment (By : Integer);
function Get return Integer;
private
Value : Integer := 0;
end T;
end Pack;
(this is far from perfect; you’d like to be able to specify the initial Value when you declare a T, but that’s starting to get complicated).
In this case, the thing to be protected is the Value. You want to be sure that if two tasks call Increment at the “same” time, one with By => 3 and one with By => 4, the Value ends up being incremented by 7.
The body could look like
package body Pack is
protected body T is
procedure Set (To : Integer) is
begin
Value := To;
end Set;
procedure Increment (By : Integer) is
begin
Value := Value + By;
end Increment;
function Get return Integer is
begin
return Value;
end Get;
end T;
end Pack;
Recommended reading: the Wikibooks section on protected types.