Rosetta Code Task "Amb" fails to compile

Hi;

I tried from the command line, did not try via Alire (yet).

Begin of TL;DR
Rosetta Code Task “Amb”, written in Ada, fails even when adding “pragma 2012”
test_amb.adb:40:13: error: equality operator must be declared before type “Amb” is frozen (RM 4.5.2 (9.8)) (Ada 2012)
End of TL;DR

Rosetta Code Task: “Amb”

Amb

 1	-- The type Amb is implemented with the operations "/" to construct it
 2	-- from strings. Each instance keeps its state. The operation Failure
 3	-- performs back tracing. Join connects two elements into a chain. The
 4	-- implementation propagates Constraint_Error when matching fails.
 5	
 6	pragma Ada_2012;
 7	with Ada.Strings.Unbounded;  use Ada.Strings.Unbounded;
 8	with Ada.Text_IO;            use Ada.Text_IO;
 9	
10	procedure Test_Amb is
11	   type Alternatives is array (Positive range <>) of Unbounded_String;
12	
13	   type Amb (Count : Positive) is record
14	      This : Positive := 1;
15	      Left : access Amb; 
16	      List : Alternatives (1..Count);
17	   end record;
18	   
19	   function Image (L : Amb) return String is
20	   begin
21	      return To_String (L.List (L.This));
22	   end Image;
23	
24	   function "/" (L, R : String) return Amb is
25	      Result : Amb (2);
26	   begin
27	      Append (Result.List (1), L);
28	      Append (Result.List (2), R);
29	      return Result;
30	   end "/";
31	   
32	   function "/" (L : Amb; R : String) return Amb is
33	      Result : Amb (L.Count + 1);
34	   begin
35	      Result.List (1..L.Count) := L.List ;
36	      Append (Result.List (Result.Count), R);
37	      return Result;
38	   end "/";
39	
40	   function "=" (L, R : Amb) return Boolean is
41	      Left : Unbounded_String renames L.List (L.This);
42	   begin
43	      return Element (Left, Length (Left)) = Element (R.List (R.This), 1);
44	   end "=";
45	   
46	   procedure Failure (L : in out Amb) is
47	   begin
48	      loop
49	         if L.This < L.Count then
50	            L.This := L.This + 1;
51	         else
52	            L.This := 1;
53	            Failure (L.Left.all);
54	         end if;
55	         exit when L.Left = null or else L.Left.all = L;
56	      end loop;
57	   end Failure;
58	
59	   procedure Join (L : access Amb; R : in out Amb) is
60	   begin
61	      R.Left := L;
62	      while L.all /= R loop
63	         Failure (R);
64	      end loop;
65	   end Join;
66	
67	   W_1 : aliased Amb := "the" / "that" / "a";
68	   W_2 : aliased Amb := "frog" / "elephant" / "thing";
69	   W_3 : aliased Amb := "walked" / "treaded" / "grows";
70	   W_4 : aliased Amb := "slowly" / "quickly";
71	begin
72	   Join (W_1'Access, W_2);
73	   Join (W_2'Access, W_3);
74	   Join (W_3'Access, W_4);
75	   Put_Line (Image (W_1) & ' ' & Image (W_2) & ' ' & Image (W_3) & ' ' & Image (W_4));
76	end Test_Amb;

Darwin Mac-mini.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:16:51 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8103 arm64

/opt/gcc-14.1.0-aarch64/bin/gnatmake

GNATMAKE 14.1.0
Copyright (C) 1995-2024, Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

/opt/gcc-14.1.0-aarch64/bin/gnatmake: Mach-O 64-bit arm64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE>

gcc -c -I./ -I- ./test_amb.adb
test_amb.adb:19:04: warning: type “Amb” is frozen by body [enabled by default]
test_amb.adb:19:04: warning: an equality operator cannot be declared after this point [enabled by default]
test_amb.adb:40:13: error: equality operator must be declared before type “Amb” is frozen (RM 4.5.2 (9.8)) (Ada 2012)
gnatmake: “./test_amb.adb” compilation error

Thanks,
Retired_Build_Engineer

Fails under Alire as well.

It compiles for me using GNAT FSF 12, so one of the two compilers is wrong.

In any case, try changing pragma 2012 to pragma 2005, because that failing rule is 2012, while the code can be compiled under 2005. Another option you can try is moving up the function "=" under the Amb type.

I’m not sure how to implement your second suggestion.

Thanks,
Retired_Build_Engineer

The common way is to make a declaration, such as:
function "=" (L, R : Amb) return Boolean;

and place that directly beneath the type declaration for Amb. You’ll need to do that for all of the functions with Amb as a paramter. The problem is the first time you show a full body of a potentially primitive operation, you can no longer add primitive operations to the type. This is why Ada generally likes to have type declarations and subprogram declarations in spec files and implementations in body files.

In order to use a type in a function, it has to be locked down with no more changes, this includes no longer adding more primitive operations (which would change the function table of the object in the functions).

I understand now :slight_smile: Like a forward declaration for mutual recursive functions.

Thanks!