Ada में generic programming — type पर contract लिखकर zero-cost reuse

· अद्यतन तिथि: · · Ada, Programming Language, Generics, Type System, Static Typing, Contract Model, Zero Cost Abstraction, GNAT, Alire, High Integrity, Code Reuse

1. शुरुआत — “सब कुछ accept करो” नहीं, “क्या promise करते हो”

Statically typed language में reusable code लिखते ही वही समस्या आती है। Integer के लिए लिखा stack string पर भी चाहिए। Floating-point array पर वही statistics चाहिए। Ascending sort की logic descending पर भी चाहिए। Type के हिसाब से वही code copy करते गए तो fix कहीं छूट जाता है। उलटा void* या cast से सब कुछ accept करने लगे तो type safety टूटती है।

Ada का जवाब generic (generic units) है।

Ada का generic mere text substitution नहीं है। Type, value, subprogram, और package को formal parameter के रूप में लेता है, और instantiation के समय statically type-check होता है। Runtime पर “क्या यह type सही है?” नहीं पूछता; compile time पर तय करता है कि “यह component अपने contract को satisfy करता है या नहीं”।

Reuse के तीन रास्तेCopy-paste, void या cast, और Ada generic — तीन reuse तरीकों का फर्क, और Ada generic पर type safety और compile-time check मिलना।Reusable logicReuse कैसे करेंCopy-pastevoid* / Object / castAda genericFix कहीं छूट जाना आसानRuntime error या type टूटना आसानType-safeCompile-time checkRuntime पर extra dispatch नहीं

इस लेख में Ada की generic programming को इस क्रम में साफ़ करते हैं।

  • Generic subprogram
  • Generic package
  • Type parameter, value parameter, subprogram parameter
  • private, range <>, digits <> जैसी type categories
  • Sort, stack, statistics, Count_If, key-value store के implementation examples
  • Formal package parameter से higher-order generic
  • Ada का contract model और practical design का तरीका

1.1 यह लेख किनके लिए है, और आप क्या लेकर जाएँगे

यह लेख इन readers के लिए है।

  • C++ templates, C# या Java generics, या Rust generics में से किसी का अनुभव हो
  • Ada की syntax पर पकड़ कम हो, पर “type पर contract लिखना” वाले design में दिलचस्पी हो
  • Long-term maintenance, embedded, या high-integrity काम में reusable component का design सोच रहे हों

Ada syntax के लिए package (specification .ads और body .adb का separation) और in / out / in out parameter modes पता हों तो आगे बढ़ सकते हैं। इन दो पर शक हो तो पहले “Ada language की खूबियाँ” पढ़ने से समझ तेज़ होती है।

जो चीज़ साथ ले जानी है, वह Ada की syntax से ज़्यादा यह design practice है: reusable component क्या माँगता है, उसे implementation से पहले type के रूप में लिख देना। यही सोच C# के interface constraints या C++20 concepts को कितना लिखना है, यह तय करते समय भी सीधे काम आती है।

1.2 पढ़ने का तरीका — पूरा पढ़ना ज़रूरी नहीं

इस लेख में 20 chapters हैं। Cover-to-cover पढ़ना ज़रूरी नहीं; मकसद के हिसाब से इस तरह उठा सकते हैं।

मकसद पढ़ें
Idea कम समय में पकड़नी हो chapter 4 (basic model) → chapter 6 (सबसे छोटा generic subprogram) → chapter 13 (contract model)
खुद component लिखना हो chapter 4 → chapter 6 → chapter 7 (generic package) → chapter 8 (behavior inject करना) → chapter 9 (type category)
केवल design decisions चाहिए chapter 13 → chapter 14 (क्या generic बनाएँ) → chapter 15 (common pitfalls) → chapter 17 (checklist)
हाथ से चलाना हो chapter 3 में environment बनाएँ, फिर chapter 6 और 7 के पूरे samples से

सबसे छोटी route chapter 4, 6 और 13 है। इन तीन में formal parameter, instantiation और contract model — यानी core — पूरा हो जाता है। Chapter 5 और 9 formal parameters की सूची हैं; ज़रूरत पड़ने पर dictionary की तरह खोलें।

यह विषय blog की series “Ada language की खूबियाँ”, “SPARK से formal verification”, “Safe concurrency”, “Real-time systems” के बाद आता है। Ada का “type से design बोलना” वाला तरीका यहाँ generic के नज़रिए से खोलते हैं।

इस लेख का knowledge map

Ada का generic type, value, subprogram और package को formal parameter के रूप में लेता है, और new से instantiation के समय statically type-check होने वाला reuse mechanism है। Generic subprogram और generic package इस mechanism के दो pillars हैं: formal type parameter private, range <> और digits <> जैसी categories से body में इस्तेमाल होने वाली operations का contract साफ़ लिखता है, formal subprogram parameter comparison या predicate जैसे behaviour inject करता है, और formal package parameter पहले से instantiate हो चुके generic package को component के रूप में लेता है। Contract model body को केवल formal parameters की सीमा में अकेले type-check करता है, इसलिए C++ templates में historically instantiation पर पहली बार error दिखने वाली problem Ada में structural रूप से उठती ही नहीं। GNAT और Alire हों तो यह mechanism मुफ्त environment में आज़माया जा सकता है।

Ada generic programming का knowledge mapGeneric subprogram और generic package type, value, subprogram और package जैसे formal parameters के contract (contract model) पर बने हैं; new से instantiation पर वही contract check होता है; C++ templates में historically जो instantiation-time error problem थी, उससे फर्क दिखाने वाला diagramimplement करता हैuse करता हैuse करता हैrequire करता हैuse करता हैuse करता हैrequire करता हैसे configurerequire करता हैrequire करता हैimplement करता हैimplement करता हैprevent करता हैcause हो सकताrequire करता हैimplement करता हैसे configureuse करता हैuse करता हैrequire करता हैimplement करता हैAda generics (generic units)Ada generic का contract modelAda (programming language)generic subprogramgeneric packageformal type (Formal Type)formal subprogram (Formal Subprogram)formal package (Formal Package)instantiationformal object (value parameter)instantiation के समय ही दिखने वाला template errorC++ templatesGNATAlire

Diagram में solid line हमेशा लागू रहने वाला relation दिखाती है और dashed line conditional relation दिखाती है (शर्तें detail page पर हर relation के explanation में दी गई हैं)। Relations की पूरी list (कुल 21, evidence और certainty सहित) तथा मुख्य concepts की definitions knowledge map की detail page पर संकलित हैं (जापानी में)। Data: JSON-LD / Turtle

2. इस लेख का नक्शा

पहले पूरा नक्शा diagram से पकड़ें। Ada generic को केवल “type argument लेने वाली feature” समझें तो नज़र बहुत संकरी रह जाती है। असल में reuse की इकाई के हिसाब से subprogram, package, subprogram parameter, value parameter और formal package parameter को मिलाते हैं।

Ada GenericsGeneric SubprogramSwapCount_IfSortGeneric PackageStackStatisticsKV StoreFormal ParametersTypeprivatelimited privaterange boxmod boxdigits boxdelta boxdiscrete boxObjectMax_SizeThresholdSubprogramLess functionEquals functionPredicatePackagewith package P is newGenericDesign IdeasContract ModelStatic CheckingZero-Cost AbstractionSeparate Specification andBody

Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।

इस लेख को पढ़ना सीधा है। पहले आधे में syntax देखते हैं, बाद में design decisions। Ada पहली बार पढ़ रहे हों तो बारीक syntax याद करने के बजाय यह देखें: “क्या चीज़ formal parameter है” और “उस formal parameter पर कौन-सी operations allow हैं”।

2.1 छोटे शब्दों का dictionary

आगे बार-बार आने वाले शब्दों को पहले English के साथ map कर देते हैं। Ada में इन्हें generic unit, generic subprogram और generic package कहते हैं; अर्थ generic ही है। इस लेख में भी वही English Ada terms रखते हैं।

इस लेख की notation English अर्थ
generic unit generic unit generic से शुरू होने वाला declaration। Generic subprogram और generic package दोनों को साथ बोलने वाला नाम
formal parameter generic formal parameter generic और declaration body के बीच लिखा, लेने वाला argument। Type, value, subprogram या package
formal part generic formal part Formal parameters की सूची वाला हिस्सा। “यहाँ contract लिखा जाता है” पढ़ सकते हैं
actual parameter generic actual parameter Instantiation पर वास्तव में दिया गया type, value, subprogram या package
instantiation instantiation new से generic unit से ordinary subprogram या package बनाना
box <> Ada में <> का नाम। range <> की तरह “concrete type instantiation पर तय होगा” बताता है
contract model contract model Formal parameters में लिखे promise की सीमा में body लिखना, और body को अकेले type-check करना — Ada का तरीका (chapter 13)

Body में Ada की notation के हिसाब से range <>, digits <> लिखते हैं, diagrams में ही range box, digits box लिखते हैं। Mermaid <> वाले label को जैसा है वैसा render नहीं करता; अर्थ body जैसा ही है। ऊपर की table के अनुसार Ada स्वयं <> को box कहता है, इसलिए diagram की wording Ada की terminology से बाहर नहीं जाती।

3. Runtime environment और compile कैसे करें

इस लेख का code GNAT 15.x या बाद मानकर है। GNAT Ada का मुख्य compiler है, और Alire से लग सकता है। Alire Ada / SPARK का package manager है; toolchain management और build के लिए भी चलता है।

gnat --version
# GNAT 15.2.1

GNAT Alire (Ada का package manager) से alr install gnat_native gprbuild चलाकर लगाएँ, और PATH में डालें।

इस लेख के samples repository में इस layout से रखे मानें।

Sample files का layoutada-generic-programming directory के अंदर src/snippets और README की file संरचना।ada-generic-programming/src/snippets/01_swap.ada02_stack.ada03_sort.ada04_statistics.ada05_filter.ada06_kv_store.adaREADME.md

कई compilation units को एक file में समेटे samples को gnatchop से बाँटकर gnatmake करते हैं।

mkdir work
cd work
gnatchop ../src/snippets/01_swap.ada
gnatmake -gnata swap_demo
./swap_demo

-gnata assertions enable करने का option है। Generic इस्तेमाल करने के लिए अनिवार्य नहीं, पर learning samples में contract और boundary conditions जाँचना आसान हो जाता है।

gnatchop से run तकएक .ada file को gnatchop से बाँटना, gnatmake से bind और link, फिर executable चलाना।executablegnatmakegnatchopdeveloperexecutablegnatmakegnatchopdeveloperएक .ada file देते हैं.ads / .adb / main में बाँटता हैgnatmake -gnata mainbind और link तक चलता है./mainrun का result

4. Ada generic का basic model

Ada generic को मोटे तौर पर इन तीन steps से सोचना आसान है।

  1. Generic unit लिखें
  2. generic हिस्से में formal parameters लिखें
  3. इस्तेमाल करने वाला new से instantiate करे
Generic का basic flowgeneric declaration, formal parameters, generic body, new से instantiation, फिर ordinary subprogram या package की तरह इस्तेमाल।generic declarationformal parametersgeneric bodynew से instantiationordinary subprogram या package की तरह इस्तेमालtypevaluesubprogrampackage

उदाहरण के लिए, दो values swap करने वाले logic को generic बनाएँ तो केवल type को formal parameter बना सकते हैं।

generic
   type Element is private;
procedure Generic_Swap (A, B : in out Element);

इस बिंदु पर Generic_Swap अभी call नहीं हो सकता। यह “किसी भी Element type पर चलने वाले swap की template” है। Concrete type देने पर ही ordinary procedure बनती है।

procedure Swap_Integer is new Generic_Swap (Integer);

Diagram में संबंध ऐसा है।

Generic_Swap की instantiationएक Generic_Swap template से Integer, Character और record type के लिए अलग-अलग ordinary procedures बनना।Integer देनाCharacter देनाMy_Record देनाGeneric_Swaptype Element is privateSwap_IntegerSwap_CharacterSwap_My_RecordInteger variables swap करनाCharacter variables swap करनाMy_Record variables swap करना

महत्वपूर्ण बात यह है कि template body केवल वही operations इस्तेमाल करे जो Element पर available हैं। type Element is private; लिखने पर assignment और equality जैसी basic operations चलती हैं, पर ordering comparison या arithmetic नहीं। यानी generic का declaration स्वयं बताता है कि “यह component क्या assume कर सकता है”।

5. Formal parameter के प्रकार — Ada generic की vocabulary

Ada generic केवल type नहीं लेता। यहीं C# या Java के आम generics से बड़ा फर्क है।

Formal parameters के चार प्रकारType, object/value, subprogram और package — Ada generic के formal parameters की चार kinds।generic formal parameterstype parameterobject / value parametersubprogram parameterpackage parametertype Element is privatetype Index is boxtype Real is digits boxMax_Size : PositiveDefault_Value : Elementwith function Less...with procedure Put ...with package P is new ...

Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।

मुख्य formal parameters table में ऐसे हैं।

प्रकार उदाहरण अर्थ
type parameter type Element is private; कोई भी definite, non-limited type लेने वाली basic form
limited type parameter type Element is limited private; Copy न हो सकने वाले type भी लेना
discrete type type Index is (<>); Integer या enumeration जैसे array index में चलने वाले type
signed integer type type Count is range <>; +, -, ordering comparison जैसी integer arithmetic assume की जा सकती है
modular integer type type Word is mod <>; Bitwise operations या modular integers
floating-point type type Real is digits <>; Float, Long_Float, user-defined floating-point type आदि
fixed-point type type Money is delta <>; Fixed-point arithmetic
value parameter Max_Size : Positive; Size या threshold जैसी चीज़ हर instance पर fix करना
subprogram with function Predicate (...) return Boolean; Comparison function या predicate जैसी behavior inject करना
package with package P is new Some_Generic (<>); पहले से instantiate हुआ generic package component के रूप में लेना

यह vocabulary होने से Ada में “सब कुछ accept करो, अंदर खतरनाक काम करो” नहीं, बल्कि “ये operations कर सकने वाले type ही लो” लिखना स्वाभाविक हो जाता है।

6. Generic subprogram — Generic_Swap से सबसे छोटी shape समझें

पहला उदाहरण: किसी भी type के दो variables swap करने वाला Generic_Swap

6.1 Specification

generic
   type Element is private;
procedure Generic_Swap (A, B : in out Element);

generic के बाद वाला हिस्सा formal parameters है। यहाँ Element नाम का type लेते हैं। is private का मतलब: generic body उस type की internal representation नहीं जानती।

इस declaration से दो बातें साफ़ हैं।

  • Generic_Swap किसी भी Element type पर चल सकता है
  • Body Element की internal structure या ordering comparison पर depend नहीं करती

6.2 Body

procedure Generic_Swap (A, B : in out Element) is
   Temp : constant Element := A;
begin
   A := B;
   B := Temp;
end Generic_Swap;

इस body में Element पर केवल assignment है। A < B भी नहीं, A + B भी नहीं। इसलिए Integer, Character, record type, enumeration — assignment चलने वाला type naturally इस्तेमाल हो जाता है।

Swap call से पहले और बादTemp के जरिए A और B की values interchange होना।call के बादcall से पहलेA = 20B = 10A = 10B = 20Temp = A

6.3 Instantiation

इस्तेमाल करने वाला new लिखता है।

procedure Swap_Int  is new Generic_Swap (Integer);
procedure Swap_Char is new Generic_Swap (Character);

अब Swap_Int और Swap_Char ordinary procedures की तरह call हो सकते हैं।

with Ada.Text_IO; use Ada.Text_IO;

procedure Swap_Demo is
   generic
      type Element is private;
   procedure Generic_Swap (A, B : in out Element);

   procedure Generic_Swap (A, B : in out Element) is
      Temp : constant Element := A;
   begin
      A := B;
      B := Temp;
   end Generic_Swap;

   procedure Swap_Int is new Generic_Swap (Integer);

   X : Integer := 10;
   Y : Integer := 20;
begin
   Put_Line ("Before: X=" & Integer'Image (X) & ", Y=" & Integer'Image (Y));
   Swap_Int (X, Y);
   Put_Line ("After : X=" & Integer'Image (X) & ", Y=" & Integer'Image (Y));
end Swap_Demo;

Run का रूप ऐसा है।

Before: X= 10, Y= 20
After : X= 20, Y= 10

यहाँ Swap_Int (X, Y); में Float variable नहीं दे सकते। Swap_Int Integer के लिए instantiate हुई ordinary procedure है। Generic कोई catch-all hole नहीं है जिसमें हर type घुस जाए; हर type के लिए एक type-safe concrete चीज़ बनाने का mechanism है — इस तरह सोचें तो बैठ जाता है।

7. Generic package — type और value को parameter बनाना

एक subprogram नहीं, कई operations और internal state को साथ reuse करना हो तो generic package इस्तेमाल करें। Classic उदाहरण stack है।

Stack में element type और max size बदल दें, तो मूल logic वही रहती है।

Generic_Stack के parameters और instancesElement type, Max_Size और operations fix रहें, Integer, Float, Unbounded_String के लिए अलग instances।Generic_StackElement_TypeMax_SizePush / Pop / Size / Is_Empty / Is_FullInt_StackElement=IntegerMax_Size=5Float_StackElement=FloatMax_Size=3String_StackElement=Unbounded_StringMax_Size=20

7.1 Specification

generic
   type Element_Type is private;
   Max_Size : Positive;
package Generic_Stack is
   procedure Push (Item : Element_Type);
   function Pop return Element_Type;
   function Is_Empty return Boolean;
   function Is_Full  return Boolean;
   function Size return Natural;

   Stack_Overflow  : exception;
   Stack_Underflow : exception;
end Generic_Stack;

यहाँ दो तरह के formal parameters हैं।

  • Element_Type type parameter है
  • Max_Size value parameter है

Max_Size Positive है, इसलिए 0 या उससे छोटी size पर instantiate नहीं कर सकते। Value parameter पर भी type वाली constraint लगा सकते हैं।

7.2 Body

package body Generic_Stack is
   subtype Index_Type is Positive range 1 .. Max_Size;
   type Storage_Type is array (Index_Type) of Element_Type;

   Data : Storage_Type;
   Top  : Natural := 0;

   procedure Push (Item : Element_Type) is
   begin
      if Top = Max_Size then
         raise Stack_Overflow;
      end if;

      Top := Top + 1;
      Data (Top) := Item;
   end Push;

   function Pop return Element_Type is
      Result : Element_Type;
   begin
      if Top = 0 then
         raise Stack_Underflow;
      end if;

      Result := Data (Top);
      Top := Top - 1;
      return Result;
   end Pop;

   function Is_Empty return Boolean is
   begin
      return Top = 0;
   end Is_Empty;

   function Is_Full return Boolean is
   begin
      return Top = Max_Size;
   end Is_Full;

   function Size return Natural is
   begin
      return Top;
   end Size;
end Generic_Stack;

इस package body में महत्वपूर्ण बात: Data और Top हर instance पर अलग बनते हैं।

package Int_Stack   is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float,   3);

ये दोनों एक ही template से बनते हैं, पर internal state share नहीं करते।

Int_Stack और Float_Stack अलग state रखते हैंएक Generic_Stack template से दो instances, प्रत्येक की अपनी Top और Data array।Float_Stack की stateTopData : Float arrayInt_Stack की stateTopData : Integer arrayGeneric_StackInt_StackFloat_Stack

7.3 Stack की state transition

Stack को state machine की तरह देखना आसान है।

Stack की state machineEmpty, NonEmpty, Full के बीच Push और Pop, और overflow/underflow transitions।PushPush / PopPop से last element निकालनाPush से Max_Size पहुँचनाPopPushPopEmptyNonEmptyFullOverflowUnderflow

7.4 Usage example

with Ada.Text_IO; use Ada.Text_IO;

procedure Stack_Demo is
   generic
      type Element_Type is private;
      Max_Size : Positive;
   package Generic_Stack is
      procedure Push (Item : Element_Type);
      function Pop return Element_Type;
      function Is_Empty return Boolean;
      function Is_Full  return Boolean;
      function Size return Natural;
      Stack_Overflow  : exception;
      Stack_Underflow : exception;
   end Generic_Stack;

   package body Generic_Stack is
      subtype Index_Type is Positive range 1 .. Max_Size;
      type Storage_Type is array (Index_Type) of Element_Type;

      Data : Storage_Type;
      Top  : Natural := 0;

      procedure Push (Item : Element_Type) is
      begin
         if Top = Max_Size then
            raise Stack_Overflow;
         end if;

         Top := Top + 1;
         Data (Top) := Item;
      end Push;

      function Pop return Element_Type is
         Result : Element_Type;
      begin
         if Top = 0 then
            raise Stack_Underflow;
         end if;

         Result := Data (Top);
         Top := Top - 1;
         return Result;
      end Pop;

      function Is_Empty return Boolean is (Top = 0);
      function Is_Full  return Boolean is (Top = Max_Size);
      function Size     return Natural is (Top);
   end Generic_Stack;

   package Int_Stack is new Generic_Stack (Integer, 5);
begin
   Int_Stack.Push (10);
   Int_Stack.Push (20);
   Int_Stack.Push (30);

   Put_Line ("Size=" & Natural'Image (Int_Stack.Size));
   Put_Line ("Pop =" & Integer'Image (Int_Stack.Pop));
   Put_Line ("Pop =" & Integer'Image (Int_Stack.Pop));
   Put_Line ("Size=" & Natural'Image (Int_Stack.Size));
end Stack_Demo;

Chapter 3 जैसा ही build करके चलाएँ।

gnatchop ../src/snippets/02_stack.ada
gnatmake -gnata stack_demo
./stack_demo
Size= 3
Pop = 30
Pop = 20
Size= 1

= के दाएँ एक space इसलिए आता है क्योंकि integer type का 'Image non-negative value से पहले एक blank character रखता है। Push तीन बार, Pop दो बार, इसलिए आखिरी Size 1 है। Stack भरा हो (इस example में Max_Size के 5 elements) और फिर Push करें तो Int_Stack.Stack_Overflow, खाली हो और Pop करें तो Int_Stack.Stack_Underflow raise होता है।

Generic package practically “छोटा container”, “fixed-size buffer”, “ring buffer”, “log queue”, “hardware abstraction layer” जैसी जगहों पर अच्छा बैठता है। खासकर Ada में, size को runtime पर vary करने से बेहतर, type या value parameter के रूप में statically fix करना high-integrity systems के साथ मेल खाता है।

8. Formal subprogram parameter — behavior inject करना

केवल type लेने से कुछ चीज़ें अभी भी व्यक्त नहीं होतीं। Sort में element type के अलावा “किसे पहले रखना है” वाली comparison logic भी चाहिए।

Ada में यह comparison function generic का formal parameter बन सकती है।

Sort में comparison function inject करनाGeneric_Insertion_Sort type, index, array और comparison function लेता है; standard comparison, Greater या custom order।Generic_Insertion_SortItem_TypeIndexItem_Arraycomparison functionstandard comparison इस्तेमालGreater देकर descendingअपना order देना

8.1 Specification

generic
   type Item_Type is private;
   type Index is (<>);
   type Item_Array is array (Index range <>) of Item_Type;
   with function "<" (Left, Right : Item_Type) return Boolean is <>;
procedure Generic_Insertion_Sort (Items : in out Item_Array);

यहाँ चार formal parameters हैं।

  1. Item_Type: array element का type
  2. Index: array index का type
  3. Item_Array: असल array type
  4. "<": comparison function

type Index is (<>); discrete type लेता है। Integer ही नहीं, enumeration भी आ सकता है। Array index केवल Positive नहीं, Day जैसी enumeration भी चल सकती है — यही Ada-style है।

with function "<" ... is <>; का is <> मतलब: actual parameter छोड़ दें तो visible standard operator या matching function इस्तेमाल होगी। यानी Integer जैसे type पर पहले से < हो तो comparison function explicit देना ज़रूरी नहीं।

8.2 Body

procedure Generic_Insertion_Sort (Items : in out Item_Array) is
   J   : Index;
   Key : Item_Type;
begin
   if Items'Length <= 1 then
      return;
   end if;

   for I in Index'Succ (Items'First) .. Items'Last loop
      Key := Items (I);
      J := I;

      while J > Items'First and then Key < Items (Index'Pred (J)) loop
         Items (J) := Items (Index'Pred (J));
         J := Index'Pred (J);
      end loop;

      Items (J) := Key;
   end loop;
end Generic_Insertion_Sort;

Insertion sort बड़े arrays के लिए सही नहीं, पर generic समझाने के लिए सही है। Comparison function बदल दें तो वही loop structure ascending और descending दोनों पर चलती है।

Insertion sort का loopबाएँ से Key निकालना, जरूरत हो तो shift, insert, अंत तक repeat।YesNoNoYesUnsorted arrayबाएँ से Key निकालनाKey पिछले element से पहले आता है?पिछले element को दाएँ सरकानाKey insert करनाआखिर तक हो गया?Sorted array

8.3 एक ही body से ascending और descending

type Int_Array is array (Positive range <>) of Integer;

procedure Sort_Asc is new Generic_Insertion_Sort
  (Item_Type  => Integer,
   Index      => Positive,
   Item_Array => Int_Array);

function Greater (Left, Right : Integer) return Boolean is
  (Left > Right);

procedure Sort_Desc is new Generic_Insertion_Sort
  (Item_Type  => Integer,
   Index      => Positive,
   Item_Array => Int_Array,
   "<"        => Greater);

Sort_Asc standard < इस्तेमाल करता है। Sort_Desc "<" => Greater लिखकर comparison function बदल देता है।

Same data, ascending और descendingSort_Asc standard comparison से, Sort_Desc Greater function से उलटा क्रम।99, 3, 47, 12Sort_Ascstandard comparisonSort_DescGreater को comparison function देना3, 12, 47, 9999, 47, 12, 3

यह mechanism C++ में comparison function object को template argument देने, या Rust में trait bounds से ordering माँगने के करीब है। फर्क यह है कि Ada में formal subprogram parameter के रूप में “इस shape का function दो” साफ़ लिखा जाता है।

8.4 Run example

Chapter 18 के layout के हिसाब से Generic_Insertion_Sort अलग files (generic_insertion_sort.ads / .adb) में हो तो caller ऐसा दिखता है।

with Ada.Text_IO;            use Ada.Text_IO;
with Generic_Insertion_Sort;

procedure Sort_Demo is
   type Int_Array is array (Positive range <>) of Integer;

   function Greater (Left, Right : Integer) return Boolean is
     (Left > Right);

   procedure Sort_Asc is new Generic_Insertion_Sort
     (Item_Type  => Integer,
      Index      => Positive,
      Item_Array => Int_Array);

   procedure Sort_Desc is new Generic_Insertion_Sort
     (Item_Type  => Integer,
      Index      => Positive,
      Item_Array => Int_Array,
      "<"        => Greater);

   procedure Show (Label : String; Items : Int_Array) is
   begin
      Put (Label);
      for V of Items loop
         Put (Integer'Image (V));
      end loop;
      New_Line;
   end Show;

   Asc  : Int_Array := (99, 3, 47, 12);
   Desc : Int_Array := (99, 3, 47, 12);
begin
   Sort_Asc (Asc);
   Sort_Desc (Desc);
   Show ("Asc :", Asc);
   Show ("Desc:", Desc);
end Sort_Demo;
gnatchop ../src/snippets/03_sort.ada
gnatmake -gnata sort_demo
./sort_demo
Asc : 3 12 47 99
Desc: 99 47 12 3

एक ही Generic_Insertion_Sort body से बनी दो procedures, comparison function बदलने भर से उलटी order में आती हैं। Elements के पहले वाला space integer type के 'Image का है, जो non-negative value से पहले एक blank रखता है।

9. Type category — private से ज़्यादा specific contract लिखना

type T is private; सुविधाजनक है, पर सब कुछ नहीं कर सकता। private type पर arithmetic या ordering comparison naturally assume नहीं की जा सकती। इसलिए Ada formal type parameter पर category specify कर सकता है।

Formal type की categoriesprivate, limited private, discrete, range, mod, digits, delta और access — body को कौन-सी operations मिलती हैं।Formal Typeprivatelimited privatediscrete box: discrete typerange box: signed integermod box: modular integerdigits box: floating-pointdelta box: fixed-pointaccess typeenumerationinteger typeFloatLong_Floatuser-defined floating-point type

Diagram में box Ada के <> (box compound delimiter) का नाम है। Mermaid <> को जैसा है वैसा render नहीं कर पाता, इसलिए diagram में ही box लिखा है।

9.1 Category specify करने से क्या फायदा है

Mean या variance निकालने के लिए addition, subtraction, multiplication, division चाहिए। private type पर ये operations assume नहीं की जा सकतीं। इसलिए floating-point type तक सीमित करते हैं।

generic
   type Real is digits <>;
   type Real_Array is array (Positive range <>) of Real;
package Generic_Statistics is
   function Mean (Values : Real_Array) return Real;
   function Variance (Values : Real_Array) return Real;
end Generic_Statistics;

type Real is digits <>; से साफ़ है कि Real floating-point type है। इसलिए generic body में +, -, *, / चल सकते हैं।

9.2 Body

package body Generic_Statistics is
   function Mean (Values : Real_Array) return Real is
      Sum : Real := 0.0;
   begin
      if Values'Length = 0 then
         return 0.0;
      end if;

      for V of Values loop
         Sum := Sum + V;
      end loop;

      return Sum / Real (Values'Length);
   end Mean;

   function Variance (Values : Real_Array) return Real is
      M   : constant Real := Mean (Values);
      Sum : Real := 0.0;
   begin
      if Values'Length = 0 then
         return 0.0;
      end if;

      for V of Values loop
         declare
            D : constant Real := V - M;
         begin
            Sum := Sum + D * D;
         end;
      end loop;

      return Sum / Real (Values'Length);
   end Variance;
end Generic_Statistics;

9.3 Float और Long_Float पर इस्तेमाल

type Float_Array is array (Positive range <>) of Float;
type Long_Array  is array (Positive range <>) of Long_Float;

package Float_Stats is new Generic_Statistics (Float, Float_Array);
package Long_Stats  is new Generic_Statistics (Long_Float, Long_Array);

वही statistics अलग precision के floating-point types पर reuse हो जाती है।

Generic_Statistics की कई instancesdigits box वाले Real से Float, Long_Float और user-defined Real पर Mean/Variance।Generic_StatisticsReal is digits boxFloat_StatsLong_StatsMy_Real_StatsFloat पर Mean / VarianceLong_Float पर Mean / Varianceuser-defined Real पर Mean / Variance

9.4 Run example

with Ada.Text_IO;        use Ada.Text_IO;
with Ada.Float_Text_IO;  use Ada.Float_Text_IO;
with Generic_Statistics;

procedure Statistics_Demo is
   type Float_Array is array (Positive range <>) of Float;

   package Float_Stats is new Generic_Statistics (Float, Float_Array);

   Samples : constant Float_Array := (1.0, 2.0, 3.0, 4.0);
begin
   Put ("Mean     = ");
   Put (Float_Stats.Mean (Samples), Fore => 1, Aft => 3, Exp => 0);
   New_Line;

   Put ("Variance = ");
   Put (Float_Stats.Variance (Samples), Fore => 1, Aft => 3, Exp => 0);
   New_Line;
end Statistics_Demo;
gnatchop ../src/snippets/04_statistics.ada
gnatmake -gnata statistics_demo
./statistics_demo
Mean     = 2.500
Variance = 1.250

Ada.Float_Text_IO standard library है जो Ada.Text_IO.Float_IO को Float पर instantiate करती है। Exp => 0 देने पर exponential form नहीं, साधारण decimal notation आती है। Aft => 3 fractional digits है। ध्यान रहे, यह Variance sample variance नहीं, population variance है (Values'Length से divide)।

9.5 Category specification एक type-level spec है

Category लिखना केवल compiler को चुप कराने की syntax नहीं है। Reader को भी बताती है कि “यह component क्या माँगता है” — यानी एक spec।

जो logic लिखनी है सही formal type वजह
Swap, store, retrieve private Assignment चलना काफी है
Copy न हो सकने वाले resource का management limited private Assignment assume न करें
Array index / enumeration states traverse करना (<>) First, Last, Succ, Pred चलते हैं
Integer sum, counter range <> Integer arithmetic assume की जा सकती है
Bitmask, wrapping counter mod <> Modular arithmetic assume की जा सकती है
Mean, variance, numeric computation digits <> Floating-point arithmetic assume की जा सकती है
Money, control quantity जैसी fixed precision delta <> Fixed-point arithmetic assume की जा सकती है

10. Predicate inject करना — Count_If को Ada-style में लिखना

Formal subprogram parameter केवल comparison function नहीं, predicate पर भी चलता है। Predicate वह function है जो value लेकर Boolean लौटाए।

C# का Func<T, bool>, Java का Predicate<T>, C++ का lambda या function object — वही भूमिका Ada में generic के formal subprogram के रूप में लिखी जाती है।

10.1 Specification

generic
   type Element is private;
   type Index is (<>);
   type Array_Type is array (Index range <>) of Element;
   with function Predicate (Item : Element) return Boolean;
function Generic_Count_If (Arr : Array_Type) return Natural;

यहाँ Predicate पर is <> नहीं लगाया। Standard रूप से visible कोई predicate function नहीं होती, इसलिए caller को हमेशा देना होगा — यही design है।

10.2 Body

function Generic_Count_If (Arr : Array_Type) return Natural is
   Count : Natural := 0;
begin
   for Item of Arr loop
      if Predicate (Item) then
         Count := Count + 1;
      end if;
   end loop;

   return Count;
end Generic_Count_If;

Flow सीधा है।

Count_If का loopArray के हर element पर Predicate, true हो तो Count बढ़ाना, अंत में Count लौटाना।TrueFalsearrayहर element traverse करनाPredicate(Item)?Count बढ़ानाकुछ न करनाअगले element परCount लौटाना

10.3 Even count और threshold count

type Int_Array is array (Positive range <>) of Integer;

function Is_Even (N : Integer) return Boolean is
  (N mod 2 = 0);

function Is_Large (N : Integer) return Boolean is
  (N > 50);

function Count_Even is new Generic_Count_If
  (Element    => Integer,
   Index      => Positive,
   Array_Type => Int_Array,
   Predicate  => Is_Even);

function Count_Large is new Generic_Count_If
  (Element    => Integer,
   Index      => Positive,
   Array_Type => Int_Array,
   Predicate  => Is_Large);

एक ही traversal logic से, केवल condition बदलकर दो functions बन जाते हैं।

एक body से दो countersGeneric_Count_If से Count_Even और Count_Large, predicate बदलने पर अलग count।Generic_Count_IfCount_EvenPredicate = Is_EvenCount_LargePredicate = Is_Large12, 7, 88, 3, 56, 91, 44, 19, 62even की संख्या50 से बड़े की संख्या

इस example में array traversal, counter का management और result लौटाना सब common है। “क्या गिनना है” केवल function के रूप में inject है। यही Ada में higher-order design की basic shape है।

10.4 Run example

with Ada.Text_IO;       use Ada.Text_IO;
with Generic_Count_If;

procedure Count_If_Demo is
   type Int_Array is array (Positive range <>) of Integer;

   function Is_Even (N : Integer) return Boolean is
     (N mod 2 = 0);

   function Is_Large (N : Integer) return Boolean is
     (N > 50);

   function Count_Even is new Generic_Count_If
     (Element    => Integer,
      Index      => Positive,
      Array_Type => Int_Array,
      Predicate  => Is_Even);

   function Count_Large is new Generic_Count_If
     (Element    => Integer,
      Index      => Positive,
      Array_Type => Int_Array,
      Predicate  => Is_Large);

   Data : constant Int_Array := (12, 7, 88, 3, 56, 91, 44, 19, 62);
begin
   Put_Line ("Even  =" & Natural'Image (Count_Even (Data)));
   Put_Line ("Large =" & Natural'Image (Count_Large (Data)));
end Count_If_Demo;
gnatchop ../src/snippets/05_filter.ada
gnatmake -gnata count_if_demo
./count_if_demo
Even  = 5
Large = 4

Data के 9 elements में even हैं 12, 88, 56, 44, 62 — पाँच; 50 से बड़े हैं 88, 56, 91, 62 — चार।

11. कई parameters को मिलाना — generic key-value store

असली components में एक type parameter अक्सर काफी नहीं होता। Key और value के type, key की comparison, max entries — कई शर्तें मिलानी पड़ती हैं।

यहाँ fixed-size, छोटा key-value store उदाहरण है।

Generic_KV_Store के parameters और इस्तेमालKey type, value type, equality function, Max_Entries, और Put/Get/Contains से settings store, cache, embedded dictionary।Generic_KV_StoreKey_TypeValue_Typekey equality functionMax_EntriesPut / Get / Containssettings storeछोटा cacheembedded के लिए fixed-size dictionary

11.1 Specification

generic
   type Key_Type is private;
   type Value_Type is private;
   with function "=" (Left, Right : Key_Type) return Boolean is <>;
   Max_Entries : Positive := 50;
package Generic_KV_Store is
   procedure Put (Key : Key_Type; Val : Value_Type);
   function Get (Key : Key_Type) return Value_Type;
   function Contains (Key : Key_Type) return Boolean;

   Key_Not_Found : exception;
   Store_Full    : exception;
end Generic_KV_Store;

इस package में चार formal parameters हैं।

Parameter प्रकार भूमिका
Key_Type type Key का type
Value_Type type Value का type
"=" subprogram Key की equality
Max_Entries value Max entries

Max_Entries पर := 50 default है। कुछ न दें तो 50-entry store बनता है।

11.2 Body

package body Generic_KV_Store is
   subtype Index_Type is Positive range 1 .. Max_Entries;

   type Key_Array   is array (Index_Type) of Key_Type;
   type Value_Array is array (Index_Type) of Value_Type;
   type Used_Array  is array (Index_Type) of Boolean;

   Keys   : Key_Array;
   Values : Value_Array;
   Used   : Used_Array := (others => False);

   function Find_Index (Key : Key_Type) return Natural is
   begin
      for I in Index_Type loop
         if Used (I) and then Keys (I) = Key then
            return I;
         end if;
      end loop;

      return 0;
   end Find_Index;

   function Find_Free return Natural is
   begin
      for I in Index_Type loop
         if not Used (I) then
            return I;
         end if;
      end loop;

      return 0;
   end Find_Free;

   procedure Put (Key : Key_Type; Val : Value_Type) is
      Pos : Natural := Find_Index (Key);
   begin
      if Pos = 0 then
         Pos := Find_Free;

         if Pos = 0 then
            raise Store_Full;
         end if;

         Used (Pos) := True;
         Keys (Pos) := Key;
      end if;

      Values (Pos) := Val;
   end Put;

   function Get (Key : Key_Type) return Value_Type is
      Pos : constant Natural := Find_Index (Key);
   begin
      if Pos = 0 then
         raise Key_Not_Found;
      end if;

      return Values (Pos);
   end Get;

   function Contains (Key : Key_Type) return Boolean is
   begin
      return Find_Index (Key) /= 0;
   end Contains;
end Generic_KV_Store;

यह implementation linear search है, इसलिए बड़े data के लिए नहीं। पर fixed size, छोटा footprint, dynamic allocation नहीं — जहाँ ये गुण मायने रखते हैं, वहाँ चलने वाली shape है।

Put और Get का प्रवाहExisting key पर value overwrite, नई key पर free slot, फिर Get से Find_Index के जरिए value लौटाना।Keys/Values/UsedGeneric_KV_Store instancecallerKeys/Values/UsedGeneric_KV_Store instancecalleralt[existing key है][नई key]Put(Key, Value)Find_Index(Key)Values(Pos) := ValueFind_FreeKeys(Pos) := KeyValues(Pos) := ValueUsed(Pos) := TrueGet(Key)Find_Index(Key)PosValues(Pos)

11.3 Instantiation example

with Ada.Text_IO;           use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Generic_KV_Store;

procedure KV_Demo is
   package Int_String_Store is new Generic_KV_Store
     (Key_Type    => Integer,
      Value_Type  => Unbounded_String,
      Max_Entries => 10);
begin
   Int_String_Store.Put (1, To_Unbounded_String ("Ada"));
   Int_String_Store.Put (2, To_Unbounded_String ("SPARK"));

   if Int_String_Store.Contains (1) then
      Put_Line ("1 => " & To_String (Int_String_Store.Get (1)));
   else
      Put_Line ("1 => (not found)");
   end if;

   --  existing key पर Put overwrite करता है
   Int_String_Store.Put (1, To_Unbounded_String ("Ada 2022"));
   Put_Line ("1 => " & To_String (Int_String_Store.Get (1)));

   if Int_String_Store.Contains (9) then
      Put_Line ("9 => " & To_String (Int_String_Store.Get (9)));
   else
      Put_Line ("9 => (not found)");
   end if;
end KV_Demo;
gnatchop ../src/snippets/06_kv_store.ada
gnatmake -gnata kv_demo
./kv_demo
1 => Ada
1 => Ada 2022
9 => (not found)

Contains के बिना Get करें और key न हो तो Key_Not_Found raise होता है। ऊपर जैसे Contains से branch करें, या exception handler लिखें।

"=" छोड़ा है। Integer पर standard equality operator है, और is <> उसी को उठाता है।

Key case-insensitive string हो तो अपनी equality function दे सकते हैं।

function Same_Key (Left, Right : Unbounded_String) return Boolean is
  (To_Lower (To_String (Left)) = To_Lower (To_String (Right)));

package String_Key_Store is new Generic_KV_Store
  (Key_Type    => Unbounded_String,
   Value_Type  => Integer,
   "="         => Same_Key,
   Max_Entries => 100);

12. Formal package parameter — generic को और component बनाना

Ada generic में package स्वयं formal parameter बन सकता है। इससे “किसी generic package से बना instance” दूसरे generic का input बन जाता है।

Stack instance को logger में देनाGeneric_Stack से Int_Stack, फिर Generic_Stack_Logger से logged instance।Generic_StackInt_StackGeneric_Stack_LoggerInt_Stack_Logger_Instance

12.1 Stack लेने वाला logger

उदाहरण: ऊपर के Generic_Stack का instance लेकर उसका size print करने वाला logger।

generic
   with package Stack is new Generic_Stack (<>);
package Generic_Stack_Logger is
   procedure Print_Size;
end Generic_Stack_Logger;

Body ऐसी है।

with Ada.Text_IO; use Ada.Text_IO;

package body Generic_Stack_Logger is
   procedure Print_Size is
   begin
      Put_Line ("Stack size =" & Natural'Image (Stack.Size));
   end Print_Size;
end Generic_Stack_Logger;

इस्तेमाल करने वाला पहले stack बनाता है, फिर वह stack logger को देता है।

package Int_Stack is new Generic_Stack
  (Element_Type => Integer,
   Max_Size     => 10);

package Int_Stack_Logger is new Generic_Stack_Logger
  (Stack => Int_Stack);

इस design से generic components एक-दूसरे से जुड़ जाते हैं।

दो चरणों में generic compositionपहले Generic_Stack instantiate, फिर वह instance Generic_Stack_Logger को देना।दूसरा चरणपहला चरणInt_Stack_LoggerGeneric_Stack_LoggerInt_StackGeneric_StackPrint_Size

C++ के template template parameters के करीब का इस्तेमाल है, पर Ada में साफ़ लिखा जा सकता है: “इस generic package का instance लो”। बड़े Ada code में container, algorithm, log, check, test helper अलग रखकर मिलाने पर सुविधा होती है।

13. Contract model — Ada generic का सबसे महत्वपूर्ण idea

Ada generic समझने में contract model केंद्र है।

Generic body केवल वही operations इस्तेमाल कर सकती है जो formal parameters ने promise की हों। उदाहरण: केवल type Element is private; लिखा हो तो Element पर < नहीं चल सकता। < चाहिए तो उसे formal subprogram के रूप में लिखें, या type category और specific करें।

Contract model के दो checksFormal part का contract, body उसी सीमा में, body अकेले type-check, instantiation पर actual parameters का check।generic formal partcontractgeneric bodycontract की सीमा में implementationbody अकेले type-checkinstantiationactual parametersactual type, function, valueactual parameters contract satisfy करते हैं?ordinary package/subprogram

इस design से generic का इस्तेमाल करने वाला ही नहीं, generic लिखने वाला भी सुरक्षित रहता है।

13.1 C++ templates से दिखने वाला फर्क

C++ templates शक्तिशाली हैं, पर historically “template body instantiate होने पर ही error दिखे” वाला व्यवहार रहा। C++20 concepts से सुधार हुआ है, पर Ada generic शुरू से ही contract को साफ़ लिखने वाला model है।

Ada contract model बनाम C++ templatesAda में formal part पर contract, body उसी में check, instantiation पर actual check; C++ में body लिखना फिर instantiation पर requirements concretize, concepts से constraint।C++ templatesinstantiation पर requirements concretizetemplate body लिखनाconcepts से constraints साफ़ की जा सकती हैंAdabody contract के अंदर checkformal part पर contract लिखनाinstantiation पर actual check

Java या C# generics में reference types, constraints, type erasure, runtime representation design का केंद्र होते हैं। Ada generic compile time पर concrete instance बनाने की तरफ़ झुकता है।

नज़रिया Ada C++ Java Rust
Contract कैसे लिखा जाता है formal part में type, value, function, package templates / concepts type parameters और bounds trait bounds
Body का check formal parameters के contract के अंदर instantiation पर concretize करना केंद्र bounds के अंदर trait bounds के अंदर
Runtime cost static resolution मूल रूप static generation मूल रूप type erasure का असर monomorphization मूल रूप
Value parameter हाँ हाँ सीमित const generics
Subprogram को formal parameter बनाना हाँ function objects आदि से lambda / function-type interface closure / function / trait
Package को formal parameter बनाना हाँ template templates आदि नहीं module structure से अलग

बारीक language features अलग हैं, पर Ada की विशेषता यह है: contract को syntax के रूप में पहले लिखना

14. Practical design decisions — क्या generic बनाना चाहिए

Generic सुविधाजनक है, पर सब कुछ generic बना देना सही नहीं। Practically इस तरह तय करें तो चूक कम होती है।

क्या generic बनाना है, कैसे तय करेंकेवल type अलग, size/threshold अलग, comparison अलग, या internal state/API साथ — हर सवाल का अगला कदम।YesNoYesNoYesNoYesNoReusable logic हैकेवल type अलग है?type parameter सोचेंSize या threshold भी अलग है?value parameter जोड़ेंComparison या decision की behavior अलग है?formal subprogram जोड़ेंInternal state या API साथ चाहिए?generic packageordinary subprogram काफी है

14.1 Generic subprogram कहाँ बैठता है

Generic subprogram stateless algorithms के लिए सही है।

  • Swap
  • Sort
  • Count_If
  • Find
  • Map जैसी transformation
  • Min / Max

Algorithm body छोटी हो, input और output साफ़ हों, तो package से subprogram ज़्यादा पढ़ने योग्य रहता है।

14.2 Generic package कहाँ बैठता है

Generic package वहाँ सही है जहाँ type के साथ कई operations और internal state चाहिए।

  • Fixed-size stack
  • Ring buffer
  • छोटा dictionary
  • Statistics suite
  • Device-wise I/O abstraction
  • Unit-tagged numeric type का operation set

Ada में package specification public API है, package body implementation — यह separation होने से generic package “type-safe module template” बन जाता है।

Spec, body और formal partFormal part spec और body दोनों को contract देता है; spec user को दिखता है, body hidden रहती है।hiddenpackage specpublic APIइस्तेमाल करने वालाpackage bodyinternal implementationgeneric formal parttype, value, function का contract

14.3 Formal parameters कम से शुरू करें

Formal parameters ज़्यादा हों तो instantiation पढ़ना मुश्किल हो जाता है। शुरू में कम रखें; बदलने की वजह निकलने पर बढ़ाएँ — यही सुरक्षित है।

--  पढ़ने में मुश्किल हो सकता है
package X is new Generic_Foo
  (A, B, C, D, E, F, G);

--  named association से intent साफ़ रखें
package X is new Generic_Foo
  (Element_Type => Integer,
   Index_Type   => Positive,
   Buffer_Size  => 128,
   "<"          => Less);

Ada में instantiation पर named association चलती है। Generic में design के महत्वपूर्ण फैसले instantiation पर दिखते हैं, इसलिए practical code में named लिखना अक्सर maintain करना आसान रखता है।

15. Common pitfalls

Ada generic शक्तिशाली है, पर पहली बार यहाँ अटकना आसान है।

15.1 private type पर comparison नहीं चलती

ऐसी body नहीं लिख सकते।

generic
   type Element is private;
function Bad_Min (A, B : Element) return Element;

function Bad_Min (A, B : Element) return Element is
begin
   if A < B then      -- यहाँ error
      return A;
   else
      return B;
   end if;
end Bad_Min;

Element केवल private declared है, इसलिए < available होना ज़रूरी नहीं। Comparison चाहिए तो contract में जोड़ें।

generic
   type Element is private;
   with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;
Comparison चाहिए तो contract में लिखेंFormal part पर comparison function लिखें तो instantiation पर comparability check होती है; केवल private हो तो body compile error।body में comparison चाहिएformal part पर comparison function लिखनाinstantiation पर comparability checkकेवल privategeneric body में compile error

15.2 is <> “सब कुछ auto-infer” नहीं है

is <> सुविधाजनक है, जादू नहीं। Instantiation point पर matching operator या subprogram visible होना चाहिए। अपनी comparison function अलग package में हो तो सही with / use करें, या named association से explicit दें — यही सुरक्षित है।

procedure Sort_By_Age is new Generic_Insertion_Sort
  (Item_Type  => Person,
   Index      => Positive,
   Item_Array => Person_Array,
   "<"        => Younger_Than);

15.3 Exception भी instance के हिसाब से अलग होती है

Generic package की specification में exception declare करें तो हर instance पर वह अलग exception बनती है।

package Int_Stack   is new Generic_Stack (Integer, 5);
package Float_Stack is new Generic_Stack (Float, 3);

यहाँ Int_Stack.Stack_Overflow और Float_Stack.Stack_Overflow अलग exceptions हैं। एक common exception चाहिए तो generic के बाहर exception define करने वाला design भी सोचें।

हर instance की अपनी exceptionGeneric_Stack की Stack_Overflow declaration से Int_Stack और Float_Stack पर अलग exceptions।अलग exceptionGeneric_StackStack_Overflow declarationInt_Stack.Stack_OverflowFloat_Stack.Stack_Overflow

15.4 Code size बढ़ सकता है

Generic runtime की अतिरिक्त indirection बचाता है, पर हर type का अपना instance बनता है, इसलिए instances ज़्यादा हों तो code size बढ़ सकता है।

यही trade-off C++ templates और Rust monomorphization में भी दिखता है। High-integrity, embedded, real-time के करीब के development में runtime uncertainty काटना होता है, बदले में build-time generated code के size को manage करते हैं।

Instantiation और code sizeएक generic body से कई type-specific instances, generated code पर runtime type test/boxing कम, instances ज़्यादा हों तो size बढ़े।एक generic bodyInteger वालाFloat वालाLong_Float वालाMy_Type वालाgenerated coderuntime type test या boxing बचाना आसानinstances ज़्यादा हों तो size पर नज़र

15.5 limited private कब इस्तेमाल करें

type Element is private; assignment assume करता है। File handle, lock, device handle जैसी चीज़ें copy न हों तो limited private सोचें।

generic
   type Resource is limited private;
   with procedure Close (R : in out Resource);
procedure Generic_Use_And_Close (R : in out Resource);

Copy न हो सकने वाले type पर, value store करने वाले container से बेहतर, procedure apply करने वाला algorithm या reference को explicit रखने वाला design सुरक्षित रहता है।

16. छोटे design patterns

यहाँ practically काम आने वाली shapes संक्षेप में हैं।

16.1 Comparable values पर ही Min देना

generic
   type Element is private;
   with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;

function Generic_Min (A, B : Element) return Element is
begin
   if A < B then
      return A;
   else
      return B;
   end if;
end Generic_Min;
Generic_Min को comparison चाहिएElement पर comparison function जरूरी, Generic_Min छोटा वाला लौटाता है।Elementcomparison function जरूरीGeneric_Minछोटा वाला लौटाना

16.2 Threshold को value parameter बनाना

generic
   type Count_Type is range <>;
   Threshold : Count_Type;
function Generic_Is_Over (Value : Count_Type) return Boolean;

function Generic_Is_Over (Value : Count_Type) return Boolean is
begin
   return Value > Threshold;
end Generic_Is_Over;

Value parameter runtime setting के लिए नहीं, instance की property के रूप में fix रखने वाली value के लिए सही है।

16.3 Output का तरीका inject करना

generic
   type Element is private;
   with procedure Put (Item : Element);
procedure Generic_Print_Twice (Item : Element);

procedure Generic_Print_Twice (Item : Element) is
begin
   Put (Item);
   Put (Item);
end Generic_Print_Twice;

इस shape पर standard output, log, test buffer — destination बदला जा सकता है।

Put को formal subprogram बनानाGeneric_Print_Twice Put लेता है; console, log या test buffer पर भेज सकते हैं।Generic_Print_TwicePut को formal subprogram के रूप में लेनाconsole outputlog outputtest buffer

16.4 Array का index type fix न करें

Ada में array का index type भी महत्वपूर्ण type जानकारी है। Positive पर lock न करें; जरूरत हो तो index type भी formal parameter बनाएँ, reuse बढ़ता है।

generic
   type Element is private;
   type Index is (<>);
   type Array_Type is array (Index range <>) of Element;
procedure Generic_Clear (Arr : in out Array_Type; Value : Element);

इस design पर Positive index वाले arrays ही नहीं, enumeration index वाले arrays भी चलते हैं।

Index discrete type के उदाहरणPositive range, Day enumeration, State enumeration, अपना integer type — Index इन सब को ले सकता है।Index is discretePositive rangeDay enumerationState enumerationअपना integer type

17. Ada-style API के लिए checklist

Generic लिखते समय आखिर में इन बिंदुओं से देखें, पढ़ना आसान रहता है।

Generic design checklistFormal parameters कम, जरूरी operations formal part में, सही category, named association, per-instance state/exception, code size, test instance।Generic design checkformal parameters कम हैं?जरूरी operations formal part में साफ़ हैं?private / range / digits जैसी category सही है?named association से पढ़कर instantiate हो सकता है?हर instance की state और exception का ख्याल है?code size बढ़ना स्वीकार है?test के लिए instance तैयार है?

शब्दों में यही है।

  • Body में इस्तेमाल होने वाली operation formal parameter के contract के रूप में दिखनी चाहिए।
  • private काफी हो तो private रखें। Arithmetic चाहिए तो range <> या digits <> इस्तेमाल करें।
  • Comparison, hash, output, conversion जैसी type-wise बदलने वाली behavior formal subprogram बनाएँ।
  • Size या threshold instance की property हो तो value parameter बनाएँ।
  • State हो तो generic package, state न हो तो generic subprogram पहले सोचें।
  • Instantiation पर arguments ज़्यादा हों तो named association इस्तेमाल करें।
  • Exception और internal state हर instance पर स्वतंत्र हैं — इसी assumption पर design करें।

18. Sample की पूरी file layout

लेख के samples को files में बाँटें तो यह layout पढ़ने में आसान रहता है।

Sample repository का file layoutgenerics में ads/adb specs, demos में callers — Generic_Swap से KV store तक।ada-generic-programmingsrcgenericsdemosgeneric_swap.adsgeneric_swap.adbgeneric_stack.adsgeneric_stack.adbgeneric_insertion_sort.adsgeneric_insertion_sort.adbgeneric_statistics.adsgeneric_statistics.adbgeneric_count_if.adsgeneric_count_if.adbgeneric_kv_store.adsgeneric_kv_store.adbswap_demo.adbstack_demo.adbsort_demo.adbstatistics_demo.adbcount_if_demo.adbkv_demo.adb

छोटे article samples के लिए एक file में रखकर gnatchop चलाना सुविधाजनक है, पर practical काम और long-term maintenance सोचें तो specification .ads और body .adb अलग रखना Ada-style layout है।

19. सार — type से reuse की सीमा तय करना

Ada की generic programming केवल “type-independent code लिखने की feature” नहीं है। असल बात यह है: reusable component क्या माँगता है, उसे type, subprogram और value के contract के रूप में साफ़ लिखना

Contract से reuse तकContract लिखना, generic body, type/value/function देकर instantiate, type-safe इस्तेमाल, बिना copy reuse।contract लिखनाgeneric body लिखनाtype, value, function देकर instantiatetype-safe इस्तेमालबिना copy reuse

इस लेख में देखा, Ada generic इन चीज़ों को formal parameter बना सकता है।

  • Type
  • Value
  • Subprogram
  • Package

Type पर private, limited private, range <>, mod <>, digits <>, delta <>, (<>) जैसी काफी बारीक categories भी लिखी जा सकती हैं। इससे generic body “पता नहीं चलेगी ऐसी operation” पर depend नहीं करती; contract में लिखी operations से सुरक्षित implementation होती है।

C या पुराने C++ assets में reuse के लिए macro, void*, function pointer, हाथ से लिखा type branching दिखता है। Ada generic उन इस्तेमालों में से बहुत को type-safe, पढ़ने योग्य रूप में बदल सकता है। खासकर long-term maintenance, embedded, real-time, high-integrity software में यह “compile time पर सीमा तय करने वाला design” बड़ा मूल्य रखता है।

20. संबंधित consulting क्षेत्र

KomuraSoft LLC Windows app development, existing assets की investigation और modification, COM / ActiveX / 32-bit / 64-bit की सीमा साफ़ करना, technical consulting और design review करता है। Ada जैसी statically typed, high-integrity के करीब की design ही नहीं — मौजूदा C/C++, C#, VB6, MFC, COM assets को कैसे साफ़ करके चलते रखना या migrate करना है, यह practically पास का विषय रहता है।

संदर्भ

  • Ada 2022 Language Reference Manual, Section 12: Generic Units — generic units का specification स्वयं। 12.1 generic declarations, 12.3 instantiation, 12.4 formal objects (value parameters), 12.5 formal types (private, range <>, digits <> जैसी categories), 12.6 formal subprograms, 12.7 formal packages। लेख के किसी chapter पर अटकें तो वही clause खोलें।
  • Ada 2022 Language Reference Manual, 2.2: Lexical Elements, Separators, and Delimiters<> को compound delimiter “box” कहा गया है। Diagram में box लिखने का आधार यही है।
  • GNAT User’s Guide for Native Platformsgnatmake, gnatchop का इस्तेमाल और -gnata समेत compile options। Chapter 3 की procedure अटके तो यहाँ देखें।
  • Alire Documentationalr लगाना, toolchain (GNAT / gprbuild) manage करना, crate बनाना। केवल environment setup हो तो यही काफी है।
  • Sample code (GitHub) — इस लेख के samples, chapter 18 के layout में files में बाँटे हुए।

निकटवर्ती विषयों में गहराई से जाने के लिए समान टैग वाले नवीनतम लेख।

ये पृष्ठ विषय को सेवाओं और निर्णयों के व्यापक संदर्भ में रखते हैं।

अक्सर पूछे जाने वाले प्रश्न

इस लेख के विषय पर परामर्श में अक्सर पूछे जाने वाले प्रश्न।

Ada में generic क्या है?
Type, value, subprogram और package को formal parameter के रूप में लेकर, new से instantiation के समय statically type-check होने वाला reuse mechanism है। यह mere text substitution नहीं है; compile time पर तय होता है कि यह component अपने contract को satisfy करता है या नहीं। Generic subprogram को केवल declare करने से call नहीं कर सकते। Concrete type देकर instantiate करने के बाद ही वह ordinary procedure या function बनता है।
Ada generics और C++ templates में क्या फर्क है?
Ada शुरू से ही contract को साफ़ लिखने वाला contract model इस्तेमाल करता है। Generic body केवल वही operations इस्तेमाल करती है जो formal parameters ने promise किए हों, और body अकेले type-check होती है। C++ templates में historically error अक्सर instantiation के समय ही दिखता था; C++20 concepts से यह बेहतर हुआ। Ada में value parameter और subprogram parameter के अलावा package को भी formal parameter बना सकते हैं, इसलिए generic components को एक-दूसरे से compose किया जा सकता है।
Ada के formal type parameter पर type category क्यों specify करते हैं?
Body में कौन-सी operations मानकर चल सकते हैं, यह contract के रूप में साफ़ करने के लिए। type T is private पर assignment और equality जैसी basic operations ही assume की जा सकती हैं; ordering comparison या arithmetic नहीं चलती। Integer arithmetic चाहिए तो range <>, floating-point arithmetic चाहिए तो digits <>, bitwise operations चाहिए तो mod <> जैसी category लिखें। Category specification reader के लिए type-level spec भी है: यह component क्या माँगता है।
Ada generics में किन बातों का ध्यान रखना चाहिए?
हर type के लिए अलग instance बनता है, इसलिए instances ज़्यादा हों तो code size बढ़ सकता है। यही trade-off C++ templates और Rust monomorphization में भी दिखता है। Generic package की specification में declared exception हर instance पर अलग exception बनती है। Formal parameters शुरू में कम रखें, बदलने की वजह निकलने पर बढ़ाएँ, और arguments ज़्यादा हों तो instantiation पर named association इस्तेमाल करें — यही practical guideline है।

लेखक की प्रोफ़ाइल

लेख के लेखक का परिचय पृष्ठ।

Go Komura

KomuraSoft LLC के प्रतिनिधि

Windows सॉफ़्टवेयर विकास, तकनीकी परामर्श और बग जाँच में विशेषज्ञ, विशेष रूप से मौजूदा सिस्टम वाली परियोजनाओं और पुनरुत्पादन में कठिन बग में।

सार्वजनिक लिंक

ब्लॉग पर लौटें