Ada-তে Generic Programming — Type দিয়ে contract লিখে zero-cost reuse
· হালনাগাদের তারিখ: · Go Komura · Ada, Programming Language, Generics, Type System, Static Typing, Contract Model, Zero Cost Abstraction, GNAT, Alire, উচ্চ নির্ভরযোগ্যতা, কোড পুনর্ব্যবহার
1. ভূমিকা — «যা খুশি নেবে» নয়, «কী প্রতিশ্রুতি দিচ্ছে»
Statically typed ভাষায় পুনর্ব্যবহারযোগ্য কোড লিখতে গেলে একই সমস্যায় ধাক্কা লাগে। Integer-এর জন্য লেখা stack string-এও ব্যবহার করতে চান। Floating-point array-তেও একই পরিসংখ্যান চালাতে চান। Ascending sort-এর logic descending-এও লাগাতে চান। কিন্তু type অনুযায়ী একই কোড কপি করতে থাকলে সংশোধন ছুটে যায়। উল্টো void* বা cast দিয়ে যা খুশি নেওয়ার ডিজাইন করলে type safety ভেঙে পড়ে।
Ada-র উত্তর generic (generic units)।
Ada-র generic সাধারণ text substitution নয়। Type, value, subprogram, এমনকি package নিজেকে formal parameter হিসেবে নেয়, আর instantiation-এর মুহূর্তে statically type-check হয়। অর্থাৎ runtime-এ «এই type-এ সত্যিই চলবে কি না» খোঁজে না; compile-time-এ «এই অংশ এই contract পূরণ করে কি না» স্থির করে।
flowchart LR
accTitle: পুনর্ব্যবহারের তিন পথ
accDescr: Copy-paste, void-star বা Object বা cast, এবং Ada generic-এর মধ্যে পুনর্ব্যবহারের উপায় ও তার ফল দেখানো চিত্র।
A[পুনর্ব্যবহার করতে চাওয়া logic] --> B{কীভাবে পুনর্ব্যবহার করবেন}
B --> C[Copy-paste]
B --> D[void* / Object / cast]
B --> E[Ada generic]
C --> C1[সংশোধন ছুটে যাওয়ার সম্ভাবনা বেশি]
D --> D1[Runtime error ও type ভাঙার সম্ভাবনা বেশি]
E --> E1[Type-safe]
E --> E2[Compile-time check]
E --> E3[Runtime-এ extra dispatch নেই]
এই নিবন্ধে Ada-র generic programming নিচের ক্রমে সাজানো হয়েছে।
- Generic subprogram
- Generic package
- Type parameter, value parameter, subprogram parameter
private,range <>,digits <>ইত্যাদি type category- Sort, stack, পরিসংখ্যান,
Count_If, key-value store-এর বাস্তবায়ন উদাহরণ - Formal package parameter দিয়ে higher-order generic
- Ada-র contract model ও বাস্তব ডিজাইনের ধারণা
1.1 কাদের জন্য, আর এখান থেকে কী নিয়ে যাবেন
এই নিবন্ধ এই পাঠকদের কথা মাথায় রেখে লেখা।
- C++ template, C# বা Java generic, অথবা Rust generic — এগুলোর কোনো একটা ব্যবহার করেছেন
- Ada-র syntax-এ গভীর নন, কিন্তু «type দিয়ে contract লেখা» ডিজাইনে আগ্রহ আছে
- দীর্ঘ রক্ষণাবেক্ষণ, embedded, উচ্চ নির্ভরযোগ্যতার ক্ষেত্রে reuse-এর অংশ কীভাবে ডিজাইন করবেন ভাবছেন
Ada syntax হিসেবে package (specification .ads ও body .adb-এর আলাদা থাকা) এবং in / out / in out parameter mode জানলেই পড়া যায়। এই দুটোতে অনিশ্চয়তা থাকলে আগে «Ada ভাষার আকর্ষণ» পড়ে নিলে বোঝা তাড়াতাড়ি হয়।
নিয়ে যাওয়ার জিনিসটা Ada-র syntax নিজে নয়, পুনর্ব্যবহারযোগ্য অংশ কী চায় তা বাস্তবায়নের আগে type হিসেবে লিখে ফেলা — এই ডিজাইন অভ্যাস। C#-এর interface constraint বা C++20 concepts কতদূর লিখবেন ঠিক করার সময়ও একই ধারণা লাগে।
1.2 পড়ার গাইড — সব অধ্যায় পড়তেই হবে না
নিবন্ধে ২০টি অধ্যায় আছে। ধারাবাহিক না পড়েও উদ্দেশ্য অনুযায়ী এভাবে তুলে পড়া যায়।
| উদ্দেশ্য | যে অধ্যায়গুলো পড়বেন |
|---|---|
| শুধু ধারণা অল্প সময়ে ধরতে চান | অধ্যায় ৪ (মৌলিক মডেল) → অধ্যায় ৬ (সবচেয়ে ছোট generic subprogram) → অধ্যায় ১৩ (contract model) |
| নিজে অংশ লিখতে চান | অধ্যায় ৪ → অধ্যায় ৬ → অধ্যায় ৭ (generic package) → অধ্যায় ৮ (behavior inject) → অধ্যায় ৯ (type category) |
| শুধু ডিজাইন সিদ্ধান্তের নির্দেশিকা চান | অধ্যায় ১৩ → অধ্যায় ১৪ (কী generic করবেন) → অধ্যায় ১৫ (যেখানে হোঁচট) → অধ্যায় ১৭ (চেকলিস্ট) |
| হাতে কলমে চালাতে চান | অধ্যায় ৩-এ পরিবেশ বানিয়ে অধ্যায় ৬ ও ৭-এর সম্পূর্ণ নমুনা থেকে |
সবচেয়ে ছোট পথ অধ্যায় ৪, ৬ ও ১৩ — এই তিনটি। এই তিনটিতে formal parameter, instantiation ও contract model — মূল জিনিসগুলো এসে যায়। অধ্যায় ৫ ও ৯ formal parameter-এর তালিকা, তাই দরকার হলে অভিধানের মতো খুলে দেখলেই চলে।
বিষয়টি এই ব্লগের ধারাবাহিক «Ada ভাষার আকর্ষণ», «SPARK দিয়ে formal verification পরিচিতি», «নিরাপদ concurrency», «Real-time সিস্টেম»-এর পরের কিস্তি। Ada-র «type দিয়ে ডিজাইন বলা» ধারণাকে generic-এর কোণ থেকে খোলা হয়েছে।
এই নিবন্ধের জ্ঞান মানচিত্র
Ada-এর জেনেরিক type · value · subprogram · package-কে formal প্যারামিটার হিসেবে নেয় এবং new দিয়ে ইনস্ট্যান্সিয়েশনের সময়েই স্ট্যাটিক টাইপ চেক হয়—এমন পুনর্ব্যবহারের ব্যবস্থা। জেনেরিক subprogram ও জেনেরিক প্যাকেজ এই ব্যবস্থার দুই স্তম্ভ; formal type প্যারামিটার private বা range<>, digits<> ইত্যাদি ক্যাটাগরি দিয়ে বডিতে যে অপারেশন ব্যবহার করা যাবে তার চুক্তি স্পষ্ট করে, formal subprogram প্যারামিটার দিয়ে তুলনা বা predicate-এর মতো আচরণ ঢুকিয়ে দেয়, আর formal package প্যারামিটার দিয়ে আগেই instantiate করা জেনেরিক প্যাকেজকে কম্পোনেন্ট হিসেবে গ্রহণ করে। এই চুক্তিকে কেবল formal প্যারামিটারের সীমার ভিতরে বডি একাই চেক করার contract model-এর ফলে, C++ টেমপ্লেট যে ঐতিহাসিকভাবে ইনস্ট্যান্সিয়েশন করে তবেই ত্রুটি জানা যায় সেই সমস্যা Ada-তে কাঠামোগতভাবে ঘটে না। GNAT ও Alire থাকলে এই ব্যবস্থা বিনামূল্যের পরিবেশে পরীক্ষা করা যায়।
flowchart LR
accTitle: Ada জেনেরিক প্রোগ্রামিংয়ের জ্ঞান মানচিত্র
accDescr: জেনেরিক subprogram ও জেনেরিক প্যাকেজ যেভাবে type · value · subprogram · package এই formal প্যারামিটারের চুক্তি (contract model)-এর উপর দাঁড়ায় এবং new দিয়ে ইনস্ট্যান্সিয়েশনে সেই চুক্তি যাচাই হয়, আর C++ টেমপ্লেট যে ঐতিহাসিকভাবে ইনস্ট্যান্সিয়েশনে গিয়ে তবেই ত্রুটি দেখাত সেই সমস্যার সঙ্গে পার্থক্য দেখানো চিত্র
ada_generics["Ada-এর জেনেরিক (generic unit)"]
ada_generic_contract_model["Ada জেনেরিকের contract model"]
ada["Ada (প্রোগ্রামিং ভাষা)"]
ada_generic_subprogram["জেনেরিক subprogram"]
ada_generic_package["জেনেরিক প্যাকেজ (generic package)"]
ada_generic_formal_type["formal type প্যারামিটার"]
ada_generic_formal_subprogram["formal subprogram প্যারামিটার"]
ada_generic_formal_package["formal package প্যারামিটার"]
ada_generic_instantiation["ইনস্ট্যান্সিয়েশন (instantiation)"]
ada_generic_formal_object["formal object (মান প্যারামিটার)"]
late_instantiation_error["ইনস্ট্যান্সিয়েশনের সময়ই ধরা পড়া টেমপ্লেট ত্রুটি"]
cpp_templates["C++ টেমপ্লেট"]
gnat["GNAT"]
alire["Alire"]
ada -->|"বাস্তবায়ন করে"| ada_generics
ada_generics -->|"ব্যবহার করে"| ada_generic_subprogram
ada_generics -->|"ব্যবহার করে"| ada_generic_package
ada_generic_subprogram -.->|"পূর্বশর্ত"| ada_generic_formal_type
ada_generics -->|"ব্যবহার করে"| ada_generic_formal_subprogram
ada_generics -->|"ব্যবহার করে"| ada_generic_formal_package
ada_generics -->|"পূর্বশর্ত"| ada_generic_instantiation
ada_generic_formal_type -->|"দিয়ে কনফিগার"| ada_generic_instantiation
ada_generic_package -.->|"পূর্বশর্ত"| ada_generic_formal_type
ada_generic_package -.->|"পূর্বশর্ত"| ada_generic_formal_object
ada_generic_formal_subprogram -->|"বাস্তবায়ন করে"| ada_generic_contract_model
ada_generic_formal_type -->|"বাস্তবায়ন করে"| ada_generic_contract_model
ada_generic_contract_model -->|"প্রতিরোধ করে"| late_instantiation_error
cpp_templates -.->|"কারণ হতে পারে"| late_instantiation_error
ada_generic_formal_package -->|"পূর্বশর্ত"| ada_generic_instantiation
gnat -->|"বাস্তবায়ন করে"| ada
gnat -.->|"দিয়ে কনফিগার"| alire
ada_generic_subprogram -.->|"ব্যবহার করে"| ada_generic_formal_subprogram
ada_generic_package -.->|"ব্যবহার করে"| ada_generic_formal_subprogram
ada_generic_instantiation -.->|"পূর্বশর্ত"| ada_generic_formal_object
ada_generic_formal_object -->|"বাস্তবায়ন করে"| ada_generic_contract_model
চিত্রে, টানা রেখা সবসময় সত্য এমন সম্পর্ককে এবং ভাঙা রেখা শর্তসাপেক্ষ সম্পর্ককে নির্দেশ করে (শর্তগুলো বিস্তারিত পাতায় প্রতিটি সম্পর্কের ব্যাখ্যায় আছে)। সম্পর্কের সম্পূর্ণ তালিকা (মোট 21, প্রমাণ ও নিশ্চয়তার মাত্রাসহ) এবং প্রধান ধারণাগুলোর সংজ্ঞা জ্ঞান মানচিত্রের বিস্তারিত পাতায় সংগ্রহ করা আছে (জাপানি ভাষায়)। তথ্য: JSON-LD / Turtle
2. এই নিবন্ধের মানচিত্র
আগে পুরো ছবিটা চিত্রে ধরুন। Ada-র generic-কে শুধু «type argument নেওয়া ফিচার» বলে বুঝলে দৃষ্টি অনেক সরু হয়ে যায়। বাস্তবে, যে এককটা পুনর্ব্যবহার করতে চান সেই অনুযায়ী subprogram, package, subprogram parameter, value parameter, formal package parameter জোড়া হয়।
mindmap
root((Ada Generics))
Generic Subprogram
Swap
Count_If
Sort
Generic Package
Stack
Statistics
KV Store
Formal Parameters
Type
private
limited private
range box
mod box
digits box
delta box
discrete box
Object
Max_Size
Threshold
Subprogram
Less function
Equals function
Predicate
Package
with package P is new Generic
Design Ideas
Contract Model
Static Checking
Zero-Cost Abstraction
Separate Specification and Body
চিত্রের box মানে Ada-র <> (box compound delimiter)। Mermaid <> আঁকতে পারে না বলে চিত্রে শুধু box লেখা।
এই নিবন্ধ পড়ার ধরন সরল। আগের ভাগে syntax, পরের ভাগে ডিজাইন সিদ্ধান্ত। Ada প্রথমবার পড়লে ছোটখাটো syntax মুখস্থ করার চেষ্টা না করে «কী formal parameter করা হয়েছে» এবং «সেই formal parameter-এ কোন operation অনুমোদিত» — এই দুটোতে নজর রাখুন।
2.1 ছোট পরিভাষা অভিধান
এরপর বারবার আসা শব্দগুলো আগে ইংরেজি ও অর্থের সঙ্গে মিলিয়ে রাখা যাক। জাপানি Ada সাহিত্যে generic-কে প্রায়ই সেই ভাষার নিজস্ব সমতুল্যে লেখা হয়; এই নিবন্ধে আমরা generic subprogram, generic package লিখি, অর্থ একই — generic।
| এই নিবন্ধের লেখা | ইংরেজি | অর্থ |
|---|---|---|
| generic unit | generic unit | generic দিয়ে শুরু হওয়া ঘোষণা। Generic subprogram ও generic package-এর মিলিত নাম |
| formal parameter | generic formal parameter | generic ও ঘোষণার body-র মাঝে লেখা, গ্রহণকারী দিকের argument। Type, value, subprogram অথবা package |
| formal part | generic formal part | Formal parameter সাজানো অংশটাই। «চুক্তি লেখার জায়গা» বলে পড়লেই চলে |
| actual parameter | generic actual parameter | Instantiation-এ সত্যি পাঠানো type, value, subprogram, package |
| instantiation | instantiation | new দিয়ে generic unit থেকে সাধারণ subprogram বা package তৈরি |
| box | <> |
Ada-তে <> চিহ্নের নাম। range <>-এর মতো «নির্দিষ্ট type instantiation-এ ঠিক হবে» বোঝায় |
| contract model | contract model | Formal parameter-এ লেখা প্রতিশ্রুতির সীমার মধ্যে body লেখা, body একাই type-check করা Ada-র পদ্ধতি (অধ্যায় ১৩) |
মূলে Ada-র লেখা অনুযায়ী range <>, digits <> লিখব, কিন্তু চিত্রে শুধু range box, digits box লেখা। Mermaid <> সহ লেবেল আঁকতে পারে না বলে; অর্থ মূলের সঙ্গে এক। উপরের সারণি অনুযায়ী Ada নিজেই <>-কে box বলে, তাই চিত্রের লেখা Ada পরিভাষার বাইরে নয়।
3. চালানোর পরিবেশ ও কম্পাইল পদ্ধতি
এই নিবন্ধের কোড GNAT 15.x বা তার পরের ধরে নেওয়া। GNAT Ada-র প্রধান compiler, Alire থেকে বসানো যায়। Alire হলো Ada / SPARK-এর package manager; toolchain ব্যবস্থাপনা ও বিল্ডেও লাগে।
gnat --version
# GNAT 15.2.1
GNAT Alire (Ada-র package manager) থেকে alr install gnat_native gprbuild দিয়ে বসিয়ে PATH-এ দিন।
এই নিবন্ধের নমুনা রিপোজিটরিতে এভাবে রাখার কথা।
flowchart TB
accTitle: নমুনার ফাইল বিন্যাস
accDescr: ada-generic-programming রিপোজিটরিতে src/snippets-এর নমুনা ফাইলগুলোর অবস্থান।
R[ada-generic-programming/] --> S[src/]
S --> N[snippets/]
N --> A[01_swap.ada]
N --> B[02_stack.ada]
N --> C[03_sort.ada]
N --> D[04_statistics.ada]
N --> E[05_filter.ada]
N --> F[06_kv_store.ada]
R --> README[README.md]
একাধিক compilation unit এক ফাইলে জড়ালে gnatchop দিয়ে ভাগ করে তারপর gnatmake করেন।
mkdir work
cd work
gnatchop ../src/snippets/01_swap.ada
gnatmake -gnata swap_demo
./swap_demo
-gnata assertion চালু করার option। Generic ব্যবহার করতে এটা বাধ্যতামূলক নয়, কিন্তু শেখার নমুনায় contract ও সীমানা শর্ত দেখা সহজ হয়।
sequenceDiagram
accTitle: gnatchop থেকে চালানো পর্যন্ত
accDescr: একটি .ada ফাইল gnatchop দিয়ে ভাগ করে gnatmake-এ বিল্ড করে চালানোর ধাপ।
participant Dev as ডেভেলপার
participant Chop as gnatchop
participant Build as gnatmake
participant Exe as এক্সিকিউটেবল
Dev->>Chop: একটি .ada ফাইল দেন
Chop-->>Dev: .ads / .adb / main-এ ভাগ
Dev->>Build: gnatmake -gnata main
Build-->>Dev: bind ও link পর্যন্ত চালায়
Dev->>Exe: ./main
Exe-->>Dev: চালানোর ফল
4. Ada generic-এর মৌলিক মডেল
Ada-র generic মোটামুটি এই তিনটে দিয়ে ভাবা সহজ।
- Generic unit লিখুন
genericঅংশে formal parameter লিখুন- ব্যবহারকারী দিকে
newদিয়ে instantiate করুন
flowchart LR
accTitle: Ada generic-এর মৌলিক মডেল
accDescr: generic ঘোষণা, formal parameter, body, এবং new দিয়ে instantiation-এর সম্পর্ক।
G[generic ঘোষণা] --> F[Formal parameter]
F --> B[Generic body]
B --> I[new দিয়ে instantiation]
I --> U[সাধারণ subprogram বা package হিসেবে ব্যবহার]
F --> F1[Type]
F --> F2[Value]
F --> F3[Subprogram]
F --> F4[Package]
উদাহরণ: দুটি মান বিনিময়ের logic generic করলে শুধু type-কে formal parameter করা যায়।
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
এই মুহূর্তে Generic_Swap এখনও ডাকা যায় না। এটা «যেকোনো Element type-এ চলবে এমন বিনিময়ের template»। নির্দিষ্ট type দিলে তবেই সাধারণ procedure হয়।
procedure Swap_Integer is new Generic_Swap (Integer);
চিত্রে সম্পর্ক এমন।
flowchart TB
accTitle: Generic_Swap-এর instantiation
accDescr: একই Generic_Swap থেকে Integer, Character, My_Record-এর জন্য আলাদা procedure তৈরি।
Template[Generic_Swap<br/>type Element is private] -->|Integer দেওয়া| SwapInt[Swap_Integer]
Template -->|Character দেওয়া| SwapChar[Swap_Character]
Template -->|My_Record দেওয়া| SwapRecord[Swap_My_Record]
SwapInt --> ICall[Integer ভেরিয়েবল বিনিময়]
SwapChar --> CCall[Character ভেরিয়েবল বিনিময়]
SwapRecord --> RCall[My_Record ভেরিয়েবল বিনিময়]
গুরুত্বপূর্ণ কথা: template-এর body শুধু Element-এ যে operation চলে সেগুলো দিয়ে লেখা। type Element is private; ঘোষণা করলে assignment বা equality-র মতো মৌলিক operation চলে, কিন্তু ছোট-বড় তুলনা বা arithmetic চলে না। অর্থাৎ generic-এর ঘোষণা নিজেই «এই অংশ কী ধরে নিতে পারে» বলে দেয়।
5. Formal parameter-এর ধরন — Ada generic-এর শব্দভাণ্ডার
Ada generic শুধু type নেয় না। C# বা Java-র সাধারণ generic-এর সঙ্গে এখানেই বড় ফারাক।
flowchart TB
accTitle: Formal parameter-এর চার ধরন
accDescr: Type, object বা value, subprogram, এবং package parameter-এর প্রতিনিধি উদাহরণ।
P[generic formal parameters] --> T[Type parameter]
P --> O[Object / value parameter]
P --> S[Subprogram parameter]
P --> PKG[Package parameter]
T --> T1[type Element is private]
T --> T2[type Index is box]
T --> T3[type Real is digits box]
O --> O1[Max_Size : Positive]
O --> O2[Default_Value : Element]
S --> S1[with function Less...]
S --> S2[with procedure Put ...]
PKG --> P1[with package P is new ...]
চিত্রের box মানে Ada-র <> (box compound delimiter)। Mermaid <> আঁকতে পারে না বলে চিত্রে শুধু box লেখা।
প্রতিনিধি formal parameter সারণিতে এমন।
| ধরন | উদাহরণ | অর্থ |
|---|---|---|
| Type parameter | type Element is private; |
যেকোনো definite, non-limited type নেওয়ার মৌলিক রূপ |
| Limited type parameter | type Element is limited private; |
কপি করা যায় না এমন type-ও নেয় |
| Discrete type | type Index is (<>); |
Integer বা enumeration — array index-এ চলে এমন type |
| Signed integer type | type Count is range <>; |
+, -, ছোট-বড় তুলনা ইত্যাদি integer arithmetic ধরে নেওয়া যায় |
| Modular integer type | type Word is mod <>; |
Bitwise ও modulo-ধর্মী integer |
| Floating-point type | type Real is digits <>; |
Float, Long_Float, ব্যবহারকারীর floating-point type ইত্যাদি |
| Fixed-point type | type Money is delta <>; |
Fixed-point arithmetic |
| Value parameter | Max_Size : Positive; |
Size বা threshold instance অনুযায়ী আটকে দেওয়া |
| Subprogram | with function Predicate (...) return Boolean; |
Comparison বা predicate — behavior inject করা |
| Package | with package P is new Some_Generic (<>); |
তৈরি হয়ে যাওয়া generic package-কে অংশ হিসেবে নেওয়া |
এই শব্দভাণ্ডার থাকায় Ada-তে «যা খুশি নেবে, ভিতরে বিপজ্জনক কাজ করবে» নয়, «যে type এই operation করতে পারে শুধু সেটাই নেবে» — এই লেখা স্বাভাবিকভাবেই আসে।
6. Generic subprogram — Generic_Swap দিয়ে সবচেয়ে ছোট গঠন
প্রথম উদাহরণ: যেকোনো type-এর দুটি ভেরিয়েবল বিনিময় করা Generic_Swap।
6.1 Specification
generic
type Element is private;
procedure Generic_Swap (A, B : in out Element);
generic-এর পরের অংশ formal parameter। এখানে Element নামের type নেওয়া হচ্ছে। is private মানে generic body থেকে দেখলে সেই type-এর অভ্যন্তরীণ উপস্থাপন জানা নেই।
এই ঘোষণা থেকে দুটো কথা বোঝা যায়।
Generic_SwapযেকোনোElementtype-এ চলে- Body
Element-এর অভ্যন্তরীণ গঠন বা ছোট-বড় তুলনার উপর নির্ভর করে না
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-এ স্বাভাবিকভাবেই চলে।
flowchart LR
accTitle: Swap-এর আগে ও পরে
accDescr: Temp দিয়ে দুটি মান বিনিময়ের ধাপ।
subgraph Before[কলের আগে]
A1[A = 10]
B1[B = 20]
end
A1 --> T[Temp = A]
B1 --> A2[A = B]
T --> B2[B = Temp]
subgraph After[কলের পরে]
A2[A = 20]
B2[B = 10]
end
6.3 Instantiation
ব্যবহারকারী দিকে new ব্যবহার করেন।
procedure Swap_Int is new Generic_Swap (Integer);
procedure Swap_Char is new Generic_Swap (Character);
এতে Swap_Int ও Swap_Char সাধারণ procedure হিসেবে ডাকা যায়।
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;
চালালে ছবি এমন।
Before: X= 10, Y= 20
After : X= 20, Y= 10
এখানে Swap_Int (X, Y);-এ Float ভেরিয়েবল দেওয়া যায় না। Swap_Int Integer-এর জন্য instantiate করা সাধারণ procedure। Generic «যা খুশি ঢোকে এমন গর্ত» নয়, «প্রতিটি type-এ নিরাপদ নির্দিষ্ট জিনিস তৈরির ব্যবস্থা» — এভাবে ধরলে বোঝা সহজ।
7. Generic package — type ও value-কে parameter করা
একটি subprogram নয়, একাধিক operation ও অভ্যন্তরীণ অবস্থা একসঙ্গে পুনর্ব্যবহার করতে চাইলে generic package ব্যবহার করেন। প্রতিনিধি উদাহরণ stack।
Stack-এর ক্ষেত্রে element type ও সর্বোচ্চ size বদলালেই মৌলিক logic একই থাকে।
flowchart TB
accTitle: Generic_Stack-এর parameter ও instance
accDescr: Element_Type ও Max_Size নিয়ে আলাদা stack instance তৈরি।
G[Generic_Stack] --> P1[Element_Type]
G --> P2[Max_Size]
G --> Ops[Push / Pop / Size / Is_Empty / Is_Full]
G --> I1[Int_Stack<br/>Element=Integer<br/>Max_Size=5]
G --> I2[Float_Stack<br/>Element=Float<br/>Max_Size=3]
G --> I3[String_Stack<br/>Element=Unbounded_String<br/>Max_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 parameter ব্যবহার করা হয়েছে।
Element_Typetype parameterMax_Sizevalue parameter
Max_Size Positive, তাই 0 বা তার কম size দিয়ে instantiate করা যায় না। Value parameter-এও type দিয়ে বাধা দেওয়া যায়।
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 থেকে তৈরি, কিন্তু অভ্যন্তরীণ অবস্থা শেয়ার করে না।
flowchart LR
accTitle: Instance অনুযায়ী আলাদা অবস্থা
accDescr: Int_Stack ও Float_Stack একই template থেকে এলেও অবস্থা শেয়ার করে না।
Template[Generic_Stack] --> IntStack[Int_Stack]
Template --> FloatStack[Float_Stack]
subgraph I[Int_Stack-এর অবস্থা]
ITop[Top]
IData[Data : Integer array]
end
subgraph F[Float_Stack-এর অবস্থা]
FTop[Top]
FData[Data : Float array]
end
IntStack --> I
FloatStack --> F
7.3 Stack-এর অবস্থা পরিবর্তন
Stack-কে state machine হিসেবে দেখলে বোঝা সহজ।
stateDiagram-v2
accTitle: Stack-এর অবস্থা পরিবর্তন
accDescr: Empty, NonEmpty, Full এবং overflow বা underflow-এর state diagram।
[*] --> Empty
Empty --> NonEmpty: Push
NonEmpty --> NonEmpty: Push / Pop
NonEmpty --> Empty: Pop দিয়ে শেষ element বের করা
NonEmpty --> Full: Push-এ Max_Size-এ পৌঁছানো
Full --> NonEmpty: Pop
Full --> Overflow: Push
Empty --> Underflow: Pop
7.4 ব্যবহারের উদাহরণ
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;
অধ্যায় ৩-এর মতোই বিল্ড করে চালান।
gnatchop ../src/snippets/02_stack.ada
gnatmake -gnata stack_demo
./stack_demo
Size= 3
Pop = 30
Pop = 20
Size= 1
=-এর ডানে একটি ফাঁকা থাকে কারণ integer type-এর 'Image অঋণাত্মক মানের আগে একটি space বসায় — এটা ভাষার নিয়ম। Push তিনবার, Pop দুবার, তাই শেষ Size 1। Stack পূর্ণ (এই উদাহরণে Max_Size 5টি) অবস্থায় আরও Push ডাকলে Int_Stack.Stack_Overflow, খালি অবস্থায় Pop ডাকলে Int_Stack.Stack_Underflow উঠে।
Generic package বাস্তবে «ছোট container», «fixed-length buffer», «ring buffer», «log-এর queue», «hardware abstraction layer»-এ ভালো কাজ করে। বিশেষ করে Ada-তে size runtime-এ নাড়িয়ে না নিয়ে type বা value parameter হিসেবে statically আটকে দেওয়া ডিজাইন উচ্চ নির্ভরযোগ্য সিস্টেমের সঙ্গে মিলে যায়।
8. Formal subprogram parameter — behavior inject করা
শুধু type নিলেও এখনও যা প্রকাশ করা যায় না তা আছে। যেমন sort-এ element type ছাড়াও «কাকে আগে সাজাবেন» — comparison logic লাগে।
Ada-তে এই comparison function-কে generic-এর formal parameter করা যায়।
flowchart LR
accTitle: Generic_Insertion_Sort-এর formal parameter
accDescr: Item_Type, Index, Item_Array এবং comparison function-এর সম্পর্ক।
A[Generic_Insertion_Sort] --> T[Item_Type]
A --> I[Index]
A --> ARR[Item_Array]
A --> CMP[Comparison function]
CMP --> ASC[মানক comparison ব্যবহার]
CMP --> DESC[Greater দিয়ে descending]
CMP --> CUSTOM[নিজস্ব ক্রম দেওয়া]
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 parameter আছে।
Item_Type: array element-এর typeIndex: array index-এর typeItem_Array: আসল array type"<": comparison function
type Index is (<>); discrete type নেয়। Integer ছাড়াও enumeration নেওয়া যায়। Array index-এ শুধু Positive নয়, Day-এর মতো enumerationও চলে — এটাই Ada-র ধরন।
with function "<" ... is <>;-এর is <> মানে actual parameter বাদ দিলে দৃশ্যমান মানক operator বা মানানসই function ব্যবহার হবে। অর্থাৎ Integer-এর মতো যে type-এ ইতিমধ্যে < আছে, comparison function না লিখলেও চলে।
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 বড় array-এ মানায় না, কিন্তু generic বোঝাতে মানায়। শুধু comparison function বদলে একই loop structure ascending-এও descending-এও চলে।
flowchart TB
accTitle: Insertion sort-এর প্রবাহ
accDescr: Key তুলে তুলনা করে সরিয়ে ঢোকানোর loop।
Start[অসাজানো array] --> Pick[বাম দিক থেকে ক্রমে Key তোলা]
Pick --> Compare{Key কি ঠিক আগের element-এর আগে?}
Compare -->|Yes| Shift[আগের element ডানে সরানো]
Shift --> Compare
Compare -->|No| Insert[Key ঢোকানো]
Insert --> Done{শেষ পর্যন্ত হয়েছে?}
Done -->|No| Pick
Done -->|Yes| End[সাজানো 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 মানক < ব্যবহার করে। অন্যদিকে Sort_Desc "<" => Greater দিয়ে comparison function বদলে দেয়।
flowchart LR
accTitle: একই body থেকে ascending ও descending
accDescr: মানক comparison এবং Greater দিয়ে একই ডেটার দুই রকম সাজানো।
Data[99, 3, 47, 12] --> A[Sort_Asc<br/>মানক comparison]
Data --> D[Sort_Desc<br/>Greater-কে comparison হিসেবে দেওয়া]
A --> AO[3, 12, 47, 99]
D --> DO[99, 47, 12, 3]
এই ব্যবস্থা C++-এ comparison function object template argument হিসেবে দেওয়া ডিজাইন, বা Rust-এ trait bound দিয়ে ক্রম চাওয়া ডিজাইনের কাছাকাছি। তবে Ada-তে formal subprogram parameter হিসেবে «এই আকারের function দেবেন» স্পষ্ট করে লেখা হয়।
8.4 চালানোর উদাহরণ
অধ্যায় ১৮-এর কাঠামো অনুযায়ী Generic_Insertion_Sort আলাদা ফাইলে (generic_insertion_sort.ads / .adb) রাখলে কলকারী দিক এমন হয়।
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 থেকে তৈরি দুটি procedure শুধু comparison বদলে উল্টো ক্রম হয়েছে — সেটা দেখা যায়। প্রতিটি মানের আগের ফাঁকাটা এই জন্য যে, integer type-এর 'Image অঋণাত্মক মানের আগে একটি space বসায়।
9. Type category — private-এর চেয়ে নির্দিষ্ট contract লেখা
type T is private; সুবিধাজনক, কিন্তু যা খুশি করা যায় না। private type-এ arithmetic বা ছোট-বড় তুলনা স্বাভাবিকভাবে ধরে নেওয়া যায় না। তাই Ada-তে formal type parameter-এ category নির্দিষ্ট করা যায়।
flowchart TB
accTitle: Formal type-এর category
accDescr: private থেকে digits box পর্যন্ত type category-র শাখা।
FormalType[Formal Type] --> Private[private]
FormalType --> Limited[limited private]
FormalType --> Discrete[discrete box: discrete type]
FormalType --> Signed[range box: signed integer]
FormalType --> Modular[mod box: modular integer]
FormalType --> Float[digits box: floating-point]
FormalType --> Fixed[delta box: fixed-point]
FormalType --> Access[access type]
Discrete --> Enum[Enumeration]
Discrete --> Int[Integer type]
Float --> F1[Float]
Float --> F2[Long_Float]
Float --> F3[ব্যবহারকারীর floating-point type]
চিত্রের box মানে Ada-র <> (box compound delimiter)। Mermaid <> আঁকতে পারে না বলে চিত্রে শুধু box লেখা।
9.1 Category নির্দিষ্ট করলে কী লাভ
যেমন গড় বা variance হিসাব করতে যোগ, বিয়োগ, গুণ, ভাগ লাগে। private type-এ এই operation ধরে নেওয়া যায় না। তাই 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);
একই পরিসংখ্যান ভিন্ন নির্ভুলতার floating-point type-এ পুনর্ব্যবহার করা যায়।
flowchart LR
accTitle: Generic_Statistics-এর instantiation
accDescr: Float, Long_Float ও ব্যবহারকারীর Real-এ একই পরিসংখ্যান।
Stats[Generic_Statistics<br/>Real is digits box] --> FS[Float_Stats]
Stats --> LS[Long_Stats]
Stats --> MS[My_Real_Stats]
FS --> FCalc[Float-এ Mean / Variance]
LS --> LCalc[Long_Float-এ Mean / Variance]
MS --> MCalc[ব্যবহারকারীর Real-এ Mean / Variance]
9.4 চালানোর উদাহরণ
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 হলো Ada.Text_IO.Float_IO-কে Float-এ instantiate করা standard library; Exp => 0 দিলে exponential নয়, সাধারণ দশমিক লেখা হয়। Aft => 3 দশমিকের পরের অঙ্ক। মনে রাখুন, এই Variance নমুনা variance নয়, population variance (Values'Length দিয়ে ভাগ)।
9.5 Category নির্দিষ্টকরণ «type-level specification»
Category নির্দিষ্টকরণ শুধু compiler চুপ করানোর syntax নয়। পাঠককেও «এই অংশ কী চায়» বলে দেওয়া specification।
| যে logic লিখতে চান | যে formal type মানায় | কারণ |
|---|---|---|
| বিনিময়, রাখা, বের করা | private |
Assignment চললেই হয় |
| কপি করা যায় না এমন resource ব্যবস্থাপনা | limited private |
Assignment ধরে নেয় না |
| Array index, enumeration অবস্থা ঘোরা | (<>) |
First, Last, Succ, Pred চলে |
| Integer যোগফল, counter | range <> |
Integer arithmetic ধরে নেওয়া যায় |
| Bitmask, circular counter | mod <> |
Modular arithmetic ধরে নেওয়া যায় |
| গড়, variance, সংখ্যাগত হিসাব | digits <> |
Floating-point arithmetic ধরে নেওয়া যায় |
| টাকা, নিয়ন্ত্রণ মান ইত্যাদি নির্দিষ্ট নির্ভুলতা | delta <> |
Fixed-point arithmetic ধরে নেওয়া যায় |
10. Predicate inject করা — Count_If-কে Ada-র মতো লেখা
Formal subprogram parameter শুধু comparison নয়, predicate-এও লাগে। Predicate মানে একটি মান নিয়ে Boolean ফেরানো function।
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 <> দেওয়া হয়নি। মানকভাবে দৃশ্যমান কোনো predicate function থাকে না, তাই ব্যবহারকারীকে অবশ্যই দিতে হবে — এই ডিজাইন।
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;
প্রবাহ সরল।
flowchart LR
accTitle: Count_If-এর প্রবাহ
accDescr: Array ঘুরে Predicate সত্য হলে Count বাড়ানো।
Arr[Array] --> Loop[প্রতিটি element ঘোরা]
Loop --> P{"Predicate(Item)?"}
P -->|True| Inc[Count বাড়ানো]
P -->|False| Skip[কিছুই না]
Inc --> Next[পরের element-এ]
Skip --> Next
Next --> Result[Count ফেরানো]
10.3 জোড় গণনা ও threshold গণনা
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 থেকে শুধু শর্ত আলাদা দুটি function তৈরি হয়।
flowchart TB
accTitle: Predicate বদলে দুটি ফাংশন
accDescr: একই Generic_Count_If থেকে জোড় ও threshold গণনা।
G[Generic_Count_If] --> E[Count_Even<br/>Predicate = Is_Even]
G --> L[Count_Large<br/>Predicate = Is_Large]
Data[12, 7, 88, 3, 56, 91, 44, 19, 62] --> E
Data --> L
E --> ER[জোড় সংখ্যার সংখ্যা]
L --> LR[50-এর চেয়ে বড়গুলোর সংখ্যা]
এই উদাহরণে array traversal, counter ব্যবস্থাপনা, ফল ফেরানো সবই সাধারণ। অন্যদিকে «কী গুনবেন» শুধু function হিসেবে inject করা। Ada-তে higher-order ডিজাইনের মৌলিক রূপ এটাই।
10.4 চালানোর উদাহরণ
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টি element-এর মধ্যে জোড় 12, 88, 56, 44, 62 — 5টি; 50-এর চেয়ে বড় 88, 56, 91, 62 — 4টি।
11. একাধিক parameter জোড়া — সাধারণ key-value store
বাস্তব অংশে শুধু একটি type parameter-এ কাজ সারা হয় না প্রায়ই। Key ও value-এর type, key তুলনার পদ্ধতি, সর্বোচ্চ সংখ্যা — একাধিক শর্ত জোড়াতে হয়।
এখানে fixed-length, সরল key-value store উদাহরণ।
flowchart TB
accTitle: Generic_KV_Store-এর parameter
accDescr: Key, Value, equality function, Max_Entries এবং ব্যবহারের জায়গা।
KV[Generic_KV_Store] --> K[Key_Type]
KV --> V[Value_Type]
KV --> EQ[Key মিলানোর function]
KV --> M[Max_Entries]
KV --> Ops[Put / Get / Contains]
Ops --> Use1[সেটিং-এর store]
Ops --> Use2[ছোট cache]
Ops --> Use3[Embedded-এর fixed-length 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 parameter আছে।
| Parameter | ধরন | ভূমিকা |
|---|---|---|
Key_Type |
Type | Key-এর type |
Value_Type |
Type | Value-এর type |
"=" |
Subprogram | Key মিলানোর পরীক্ষা |
Max_Entries |
Value | সর্বোচ্চ entry সংখ্যা |
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;
এই বাস্তবায়ন linear search, তাই বিপুল ডেটার জন্য নয়। তবে fixed-length, ছোট আকার, dynamic memory allocation নেই — এই ধর্ম যেখানে জরুরি, সেখানে ব্যবহারযোগ্য রূপ।
sequenceDiagram
accTitle: Put ও Get-এর ধাপ
accDescr: বিদ্যমান key আপডেট বনাম নতুন key ঢোকানোর sequence।
participant App as কলকারী দিক
participant Store as Generic_KV_Store instance
participant Data as Keys/Values/Used
App->>Store: Put(Key, Value)
Store->>Data: Find_Index(Key)
alt বিদ্যমান key আছে
Store->>Data: Values(Pos) := Value
else নতুন key
Store->>Data: Find_Free
Store->>Data: Keys(Pos) := Key
Store->>Data: Values(Pos) := Value
Store->>Data: Used(Pos) := True
end
App->>Store: Get(Key)
Store->>Data: Find_Index(Key)
Data-->>Store: Pos
Store-->>App: Values(Pos)
11.3 Instantiation-এর উদাহরণ
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;
-- বিদ্যমান 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 উঠে। উপরের মতো Contains দিয়ে শাখা করুন, অথবা exception handler লিখুন — দুইয়ের একটা বেছে নিন।
"=" বাদ দেওয়া হয়েছে। Integer-এ মানক equality operator আছে, is <> দিয়ে সেটাই ব্যবহৃত হয়।
Key যদি বড়-ছোট হাত উপেক্ষা করা 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-কে আরও কম্পোনেন্ট হিসেবে জোড়া
Ada generic-এ package নিজেকে formal parameter করা যায়। এতে «কোনো generic package থেকে তৈরি instance»-কে অন্য generic-এর ইনপুট হিসেবে ধরা যায়।
flowchart LR
accTitle: Formal package parameter
accDescr: Generic_Stack instance-কে Generic_Stack_Logger-এ দেওয়া।
GS[Generic_Stack] --> IS[Int_Stack]
IS --> Logger[Generic_Stack_Logger]
Logger --> Logged[Int_Stack_Logger_Instance]
12.1 Stack নেওয়া logger
যেমন আগের Generic_Stack-এর instance নিয়ে তার size দেখানো 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);
এই ডিজাইনে generic অংশগুলোকে একে অপরের সঙ্গে জোড়া যায়।
flowchart TB
accTitle: Generic-কে স্তরে জোড়া
accDescr: প্রথম স্তরে stack, দ্বিতীয় স্তরে logger।
subgraph Layer1[প্রথম স্তর]
T1[Generic_Stack] --> I1[Int_Stack]
end
subgraph Layer2[দ্বিতীয় স্তর]
T2[Generic_Stack_Logger] --> I2[Int_Stack_Logger]
end
I1 --> T2
I2 --> API[Print_Size]
C++-এর template template parameter-এর কাছাকাছি ব্যবহার, কিন্তু Ada-তে «এই generic package-এর instance নেবে» স্পষ্ট করে লেখা যায়। বড় Ada কোডে container, algorithm, log, পরীক্ষা, test সহায়তা আলাদা করে জোড়াতে সুবিধা হয়।
13. Contract Model — Ada generic-এর সবচেয়ে জরুরি ধারণা
Ada generic বুঝতে contract model জরুরি।
Generic-এর body শুধু formal parameter যে operation-এর প্রতিশ্রুতি দিয়েছে সেগুলো দিয়ে লিখতে হয়। যেমন শুধু type Element is private; ঘোষণা করে Element-এ < ব্যবহার করা যায় না। < চাইলে formal subprogram হিসেবে স্পষ্ট করতে হয়, অথবা type category আরও নির্দিষ্ট করতে হয়।
flowchart TB
accTitle: Contract model-এর পরীক্ষা
accDescr: Formal part-এর চুক্তির ভিতরে body check, instantiation-এ actual check।
Spec[generic formal part<br/>চুক্তি] --> Body[generic body<br/>চুক্তির সীমায় বাস্তবায়ন]
Body --> Check1[Body একাই type-check]
Spec --> Inst[Instantiation]
Actual[actual parameters<br/>আসল type / function / value] --> Inst
Inst --> Check2[Actual parameter চুক্তি পূরণ করে কি না]
Check2 --> Instance[সাধারণ package / subprogram]
এই ডিজাইনে generic-এর ব্যবহারকারীই নয়, generic লেখার দিকও রক্ষা পায়।
13.1 C++ template-এর সঙ্গে দেখার ফারাক
C++ template শক্তিশালী, কিন্তু historically «template body instantiate করলে তবেই error ওঠে» — এই ধর্ম ছিল। C++20-এর concepts সেটা উন্নত করেছে, তবে Ada generic শুরু থেকেই চুক্তি স্পষ্ট করার মডেল।
flowchart LR
accTitle: Ada ও C++ template-এর দেখা
accDescr: Ada আগে contract লেখে, C++ historically instantiation-এ error দেখায়।
subgraph Ada[Ada]
A1[formal part-এ চুক্তি লেখা] --> A2[body চুক্তির ভিতরে check]
A2 --> A3[instantiation-এ actual check]
end
subgraph CPP[C++ templates]
C1[template body লেখা] --> C2[instantiation-এ প্রয়োজনীয় expression নির্দিষ্ট হয়]
C2 --> C3[concepts দিয়ে বাধা স্পষ্ট করা যায়]
end
Java বা C#-এর generic-এ reference type, constraint, type erasure, runtime উপস্থাপনের সম্পর্ক ডিজাইনের কেন্দ্র। অন্যদিকে Ada-র generic compile-time-এ নির্দিষ্ট instance তৈরির ধারণার দিকে ঝোঁকে।
| দৃষ্টিভঙ্গি | Ada | C++ | Java | Rust |
|---|---|---|---|---|
| চুক্তি লেখার ধরন | formal part-এ type, value, function, package লেখা | templates / concepts | Type parameter ও bounds | trait bounds |
| Body-র check | Formal parameter-এর চুক্তির ভিতরে | Instantiation-এ নির্দিষ্টকরণই মূল | bounds-এর ভিতরে | trait bounds-এর ভিতরে |
| Runtime খরচ | Static resolutionই মূল | Static generationই মূল | Type erasure-এর প্রভাব | Monomorphizationই মূল |
| Value parameter | আছে | আছে | সীমিত | const generics |
| Subprogram-কে formal parameter করা | আছে | Function object ইত্যাদি দিয়ে | Lambda / function-type interface | Closure / function / trait |
| Package-কে formal parameter করা | আছে | Template template ইত্যাদি | নেই | Module গঠন আলাদা বিষয় |
ছোটখাটো ভাষা ফিচার আলাদা, কিন্তু Ada-র বৈশিষ্ট্য «চুক্তি syntax হিসেবে আগে লেখা»।
14. বাস্তবে ডিজাইন সিদ্ধান্ত — কী generic করা উচিত
Generic সুবিধাজনক, কিন্তু যা খুশি generic করলেই ভালো হয় না। বাস্তবে এভাবে সিদ্ধান্ত নিলে ব্যর্থতা কম।
flowchart TB
accTitle: কী generic করবেন তার সিদ্ধান্ত
accDescr: Type, value, subprogram, package-এর মধ্যে বেছে নেওয়ার প্রশ্নমালা।
Start[পুনর্ব্যবহার করতে চাওয়া logic আছে] --> Q1{শুধু type আলাদা?}
Q1 -->|Yes| GType[Type parameter ভাবা]
Q1 -->|No| Q2{Size বা threshold-ও আলাদা?}
Q2 -->|Yes| GObject[Value parameter যোগ]
Q2 -->|No| Q3{তুলনা বা বিচারের behavior আলাদা?}
Q3 -->|Yes| GSubp[Formal subprogram যোগ]
Q3 -->|No| Q4{অভ্যন্তরীণ অবস্থা বা API একত্র করতে চান?}
Q4 -->|Yes| GPackage[Generic package]
Q4 -->|No| Normal[সাধারণ subprogramই যথেষ্ট]
14.1 Generic subprogram যেখানে মানায়
Generic subprogram অবস্থাহীন algorithm-এ মানায়।
SwapSortCount_IfFindMap-ধর্মী রূপান্তরMin/Max
Algorithm-এর body ছোট, ইনপুট ও আউটপুট স্পষ্ট হলে package-এর চেয়ে subprogram পড়া সহজ।
14.2 Generic package যেখানে মানায়
Generic package type-এর সঙ্গে একাধিক operation বা অভ্যন্তরীণ অবস্থা রাখতে চাইলে মানায়।
- Fixed-length stack
- Ring buffer
- ছোট dictionary
- পরিসংখ্যানের সেট
- Device অনুযায়ী I/O abstraction
- এককসহ সংখ্যা type-এর operation সেট
বিশেষ করে Ada-তে package specification প্রকাশ্য API, package body বাস্তবায়ন — এই আলাদা থাকা থাকায় generic package «type-safe module template» হিসেবে ব্যবহার করা যায়।
flowchart LR
accTitle: Generic package-এর spec ও body
accDescr: Formal part চুক্তি, spec প্রকাশ্য API, body লুকানো বাস্তবায়ন।
Spec[package spec<br/>প্রকাশ্য API] --> User[ব্যবহারকারী দিক]
Body[package body<br/>অভ্যন্তরীণ বাস্তবায়ন] -.লুকানো.-> User
Formal[generic formal part<br/>type / value / function-এর চুক্তি] --> Spec
Formal --> Body
14.3 Formal parameter কম করে শুরু করুন
Formal parameter বাড়িয়ে ফেললে instantiation পড়া কঠিন হয়। প্রথমে ন্যূনতম রাখুন, বদলাবার কারণ দেখা দিলে বাড়ান — সেটাই নিরাপদ।
-- পড়া কঠিন হয়ে ওঠার উদাহরণ
package X is new Generic_Foo
(A, B, C, D, E, F, G);
-- named association দিয়ে উদ্দেশ্য রাখা
package X is new Generic_Foo
(Element_Type => Integer,
Index_Type => Positive,
Buffer_Size => 128,
"<" => Less);
Ada-তে instantiation-এ named association ব্যবহার করা যায়। Generic-এ ডিজাইনের জরুরি জায়গা instantiation-এ দেখা যায়, তাই বাস্তব কোডে named করে লেখা রক্ষণাবেক্ষণে সহজ হয় প্রায়ই।
15. যেখানে প্রায়ই হোঁচট
Ada generic শক্তিশালী, কিন্তু শুরুতে যেখানে হোঁচট লাগে তা আছে।
15.1 private type-এ ছোট-বড় তুলনা চলে না
এমন 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 বলে ঘোষিত, তাই < চলবেই এমন নয়। তুলনা করতে চাইলে চুক্তিতে যোগ করেন।
generic
type Element is private;
with function "<" (Left, Right : Element) return Boolean is <>;
function Generic_Min (A, B : Element) return Element;
flowchart LR
accTitle: private-এ তুলনা চলে না
accDescr: Formal part-এ comparison না থাকলে body-তে compile error।
Need[Body-তে তুলনা ব্যবহার করতে চান] --> Contract[formal part-এ comparison function লেখা]
Contract --> OK[Instantiation-এ তুলনাযোগ্যতা যাচাই]
Need --> NoContract[শুধু private]
NoContract --> Error[Generic body-তে compile error]
15.2 is <> «যা খুশি auto-infer» নয়
is <> সুবিধাজনক, কিন্তু জাদু নয়। Instantiation-এর জায়গায় মানানসই operator বা subprogram দৃশ্যমান থাকতে হয়। নিজস্ব comparison আলাদা package-এ রাখলে যথাযথ with বা use করুন, অথবা named করে স্পষ্ট করে দেওয়াই নিরাপদ।
procedure Sort_By_Age is new Generic_Insertion_Sort
(Item_Type => Person,
Index => Positive,
Item_Array => Person_Array,
"<" => Younger_Than);
15.3 Instance অনুযায়ী exception-ও আলাদা হয়
Generic package-এর specification-এ exception ঘোষণা করলে প্রতিটি 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 আলাদা exception। সাধারণ exception হিসেবে ধরতে চাইলে generic-এর বাইরে exception সংজ্ঞায়িত করার ডিজাইনও ভাবা যায়।
flowchart TB
accTitle: Instance অনুযায়ী আলাদা exception
accDescr: Int_Stack ও Float_Stack-এর Stack_Overflow আলাদা exception।
Generic[Generic_Stack<br/>Stack_Overflow ঘোষণা] --> I[Int_Stack.Stack_Overflow]
Generic --> F[Float_Stack.Stack_Overflow]
I -.আলাদা exception.-> F
15.4 Code size বাড়তে পারে
Generic runtime-এ অতিরিক্ত indirection এড়াতে সাহায্য করে, অন্যদিকে প্রতিটি type-এ instance তৈরি হয় বলে instance বেশি হলে code size বাড়তে পারে।
C++ template ও Rust-এর monomorphization-এও এই trade-off দেখা যায়। উচ্চ নির্ভরযোগ্য, embedded, real-time-এর দিকের ডেভেলপমেন্টে runtime-এর অনিশ্চয়তা কমানোর বদলে বিল্ড-এর তৈরি হওয়া অংশের সাইজ সামলানো — এই চিন্তা আসে।
flowchart LR
accTitle: Instantiation ও কোড সাইজ
accDescr: প্রতিটি instance আলাদা কোড তৈরি করে সাইজ বাড়তে পারে।
Generic[একটি generic body] --> I1[Integer সংস্করণ]
Generic --> I2[Float সংস্করণ]
Generic --> I3[Long_Float সংস্করণ]
Generic --> I4[My_Type সংস্করণ]
I1 --> Code[তৈরি কোড]
I2 --> Code
I3 --> Code
I4 --> Code
Code --> Pros[Runtime type পরীক্ষা বা boxing এড়ানো সহজ]
Code --> Cons[Instance বেশি হলে সাইজ বৃদ্ধি খেয়াল রাখুন]
15.5 limited private যেখানে ব্যবহার করা উচিত
type Element is private; assignment ধরে নেয়। File handle, lock, device handle — কপি হোক চান না এমন জিনিস সামলাতে limited private ভাবুন।
generic
type Resource is limited private;
with procedure Close (R : in out Resource);
procedure Generic_Use_And_Close (R : in out Resource);
কপি করা যায় না এমন type সামলানো ডিজাইনে মান রাখে এমন container-এর চেয়ে, procedure প্রয়োগ করা algorithm বা reference স্পষ্ট রাখা ডিজাইন নিরাপদ।
16. ছোট ডিজাইন প্যাটার্নের সংকলন
এখান থেকে বাস্তবে প্রায় ব্যবহার হওয়া রূপ সংক্ষেপে সাজানো।
16.1 তুলনাযোগ্য মানেই শুধু 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;
flowchart LR
accTitle: তুলনাযোগ্য মানে Min
accDescr: Comparison function থাকলেই Generic_Min ছোট মান ফেরায়।
T[Element] --> C[Comparison function লাগে]
C --> M[Generic_Min]
M --> R[ছোট মান ফেরানো]
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-এর সেটিং নয়, instance-এর ধর্ম হিসেবে আটকে রাখতে চাওয়া মানে মানায়।
16.3 আউটপুট মাধ্যম 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;
এই রূপে standard output, log, test-এর buffer — আউটপুট গন্তব্য বদলানো যায়।
flowchart TB
accTitle: আউটপুট মাধ্যম inject করা
accDescr: Put-কে formal subprogram করে console, log, test buffer বদলানো।
Print[Generic_Print_Twice] --> Put[Put-কে formal subprogram হিসেবে নেওয়া]
Put --> Console[Console আউটপুট]
Put --> Log[Log আউটপুট]
Put --> Test[Test-এর buffer]
16.4 Array-এর index type আটকে না রাখা
Ada-তে array-এর index typeও জরুরি type তথ্য। Positive ধরে না নিয়ে দরকারে index type-ও formal parameter করলে পুনর্ব্যবহার বাড়ে।
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);
এই ডিজাইনে Positive index-এর array ছাড়াও enumeration index-এর array চলে।
flowchart LR
accTitle: Array index type আটকে না রাখা
accDescr: Discrete Index-এ Positive, enumeration, নিজস্ব integer চলে।
Index[Index is discrete] --> Positive[Positive range]
Index --> Day[Day enumeration]
Index --> State[State enumeration]
Index --> Slot[নিজস্ব integer type]
17. Ada-র মতো API-এর চেকলিস্ট
Generic লেখার শেষে এই দৃষ্টিভঙ্গিতে ফিরে দেখলে পড়া সহজ হয়।
flowchart TB
accTitle: Generic API চেকলিস্ট
accDescr: Formal parameter কমানো থেকে test instance পর্যন্ত সাতটি দৃষ্টিভঙ্গি।
C[Generic ডিজাইন চেক] --> C1[Formal parameter ন্যূনতম কি]
C --> C2[দরকারি operation formal part-এ স্পষ্ট কি]
C --> C3[private / range / digits ইত্যাদি category ঠিক কি]
C --> C4[Named association দিয়ে পড়া যায় এমন instantiation কি]
C --> C5[Instance অনুযায়ী অবস্থা ও exception মাথায় আছে কি]
C --> C6[Code size বৃদ্ধি সহ্য করা যায় কি]
C --> C7[Test-এর instance তৈরি আছে কি]
বাক্যে গুছিয়ে এমন।
- Body-তে ব্যবহার করা operation অবশ্যই formal parameter-এর চুক্তি হিসেবে দেখা যাক।
private-এ চললেprivateরাখুন। Arithmetic লাগলেrange <>বাdigits <>ব্যবহার করুন।- তুলনা, hash, আউটপুট, রূপান্তর — type অনুযায়ী বদলানো behavior formal subprogram করুন।
- Size বা threshold instance-এর ধর্ম হলে value parameter করুন।
- অবস্থা থাকলে generic package, না থাকলে generic subprogram আগে ভাবুন।
- Instantiation-এ argument যত বেশি, named association তত ব্যবহার করুন।
- Exception ও অভ্যন্তরীণ অবস্থা প্রতিটি instance-এ স্বাধীন — এই ধরে ডিজাইন করুন।
18. নমুনার সম্পূর্ণ কাঠামোর উদাহরণ
নিবন্ধের নমুনা ফাইলে ভাগ করলে এমন কাঠামো পড়া সহজ করে।
flowchart TB
accTitle: সম্পূর্ণ নমুনার ফাইল কাঠামো
accDescr: generics ও demos-এ spec/body ও demo ফাইল ভাগ।
Root[ada-generic-programming] --> Src[src]
Src --> G[generics]
Src --> D[demos]
G --> SwapSpec[generic_swap.ads]
G --> SwapBody[generic_swap.adb]
G --> StackSpec[generic_stack.ads]
G --> StackBody[generic_stack.adb]
G --> SortSpec[generic_insertion_sort.ads]
G --> SortBody[generic_insertion_sort.adb]
G --> StatsSpec[generic_statistics.ads]
G --> StatsBody[generic_statistics.adb]
G --> CountSpec[generic_count_if.ads]
G --> CountBody[generic_count_if.adb]
G --> KVSpec[generic_kv_store.ads]
G --> KVBody[generic_kv_store.adb]
D --> SwapDemo[swap_demo.adb]
D --> StackDemo[stack_demo.adb]
D --> SortDemo[sort_demo.adb]
D --> StatsDemo[statistics_demo.adb]
D --> CountDemo[count_if_demo.adb]
D --> KVDemo[kv_demo.adb]
ছোট নিবন্ধের নমুনা হলে এক ফাইলে জড়িয়ে gnatchop করাও সুবিধাজনক, কিন্তু বাস্তব ও দীর্ঘ রক্ষণাবেক্ষণ ভাবলে specification .ads ও body .adb আলাদা রাখাই Ada-র মতো কাঠামো।
19. সারাংশ — type দিয়ে reuse-এর সীমানা ঠিক করা
Ada-র generic programming শুধু «type-এর উপর নির্ভর করে না এমন কোড লেখার ফিচার» নয়। আসল কথা, পুনর্ব্যবহারযোগ্য অংশ কী চায় তা type, subprogram ও value-এর চুক্তি হিসেবে স্পষ্ট করা।
flowchart LR
accTitle: চুক্তি থেকে reuse পর্যন্ত
accDescr: Contract লেখা, body লেখা, instantiation, type-safe reuse।
Contract[চুক্তি লেখা] --> Generic[Generic body লেখা]
Generic --> Instance[Type, value, function দিয়ে instantiate]
Instance --> Safe[Type-safe ব্যবহার]
Safe --> Reuse[কপি ছাড়া পুনর্ব্যবহার]
এই নিবন্ধে দেখা গেছে, Ada generic-এ নিচের জিনিস formal parameter করা যায়।
- Type
- Value
- Subprogram
- Package
আর type-এর জন্য private, limited private, range <>, mod <>, digits <>, delta <>, (<>) — বেশ নির্দিষ্ট category দেওয়া যায়। এতে generic body «চলবে কি না জানা নেই এমন operation»-এর উপর নির্ভর করে না, চুক্তিতে লেখা operation দিয়েই নিরাপদে বাস্তবায়িত হয়।
C বা পুরোনো C++ সম্পদে reuse-এর জন্য macro, void*, function pointer, হাতে লেখা type শাখা ব্যবহার হয় কখনো। Ada-র generic সেই ব্যবহারের অনেকটা type-safe, পড়া যায় এমন রূপে বদলে দিতে পারে। বিশেষ করে দীর্ঘ রক্ষণাবেক্ষণ, embedded, real-time, উচ্চ নির্ভরযোগ্য সফটওয়্যারে «compile-time-এ সীমানা ঠিক করা ডিজাইন» বড় মূল্য রাখে।
20. সংশ্লিষ্ট পরামর্শের ক্ষেত্র
KomuraSoft LLC Windows অ্যাপ ডেভেলপমেন্ট, বিদ্যমান সম্পদ খতিয়ে দেখা ও সংশোধন, COM / ActiveX / 32bit / 64bit সীমানা গুছানো, প্রযুক্তি পরামর্শ ও ডিজাইন রিভিউ নিয়ে কাজ করে। Ada-র মতো statically typed, উচ্চ নির্ভরযোগ্যতার দিকের ডিজাইনই নয়, বিদ্যমান C/C++, C#, VB6, MFC, COM সম্পদ কীভাবে গুছিয়ে বাঁচিয়ে রাখা বা সরানো যায় — বাস্তবে সেটাও কাছাকাছি বিষয় হয়ে ওঠে।
- Windows অ্যাপ ডেভেলপমেন্ট
- COM কম্পোনেন্ট ডেভেলপমেন্ট
- Windows অ্যাপ্লিকেশন প্রতিস্থাপন
- প্রযুক্তি পরামর্শ ও ডিজাইন রিভিউ
- যোগাযোগ
তথ্যসূত্র
- Ada 2022 Language Reference Manual, Section 12: Generic Units — generic unit-এর বিধান নিজেই। 12.1 generic ঘোষণা, 12.3 instantiation, 12.4 formal object (value parameter), 12.5 formal type (
private,range <>,digits <>ইত্যাদি category), 12.6 formal subprogram, 12.7 formal package। এই নিবন্ধের কোনো অধ্যায়ে দ্বিধা হলে সংশ্লিষ্ট অনুচ্ছেদ খোলাই নিশ্চিত পথ। - Ada 2022 Language Reference Manual, 2.2: Lexical Elements, Separators, and Delimiters —
<>-কে compound delimiter «box» বলা হয় যেখানে সংজ্ঞায়িত। চিত্রেরboxলেখার ভিত্তি। - GNAT User’s Guide for Native Platforms —
gnatmake,gnatchop-এর ব্যবহার এবং-gnataসহ compile option-এর তালিকা। অধ্যায় ৩-এর ধাপে আটকে গেলে এখানে দেখুন। - Alire Documentation —
alrবসানো, toolchain (GNAT / gprbuild) ব্যবস্থাপনা, crate তৈরির পদ্ধতি আছে। শুধু পরিবেশ গড়তে এই নথিই যথেষ্ট। - নমুনা কোড (GitHub) — এই নিবন্ধের নমুনা অধ্যায় ১৮-এর কাঠামোয় ফাইলে ভাগ করা।
সম্পর্কিত নিবন্ধ
কাছাকাছি বিষয়ে গভীরে যেতে একই ট্যাগযুক্ত সাম্প্রতিক নিবন্ধ।
Ada দিয়ে রিয়েল-টাইম সিস্টেম প্রোগ্রামিং ── priority, period ও execution time নিয়ন্ত্রণ বাস্তবে
Ada-এর Annex D (রিয়েল-টাইম সিস্টেম) আটটি ব্যবহারিক কোড উদাহরণ দিয়ে শেখা যায়। টাস্ক priority, Ceiling_Locking, delay until দিয়ে period...
Ada-তে নিরাপদ concurrency ── টাস্ক ও protected object-এর ব্যবহারিক গাইড
Ada ভাষায় বসানো concurrency—টাস্ক ও protected object—এর পরিচিতিমূলক নিবন্ধ। rendezvous (entry/accept), selective accept, protected objec...
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ৩) — সেকেন্ডে বুট হওয়া ভার্চুয়াল মেশিন: WSL2, Windows Sandbox ও কন্টেইনার এত হালকা কেন
WSL2 ও Windows Sandbox সেকেন্ডে শুরু হয়ে এত হালকা মনে হয় কেন? এই নিবন্ধ ডায়নামিক বেস ইমেজ ও ডাইরেক্ট ম্যাপ থেকে ডায়নামিক মেমরি বরাদ্দ...
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ২) — কার্নেলও দেখতে পায় না এমন মেমরি: VBS, HVCI ও Credential Guard কীভাবে কাজ করে
সামঞ্জস্যপূর্ণ হার্ডওয়্যারে ক্লিন ইনস্টলে VBS ডিফল্টে চালু থাকে এবং হাইপারভাইজার ও SLAT দিয়ে কার্নেলের চেয়ে শক্তিশালী আইসোলেশন তৈরি কর...
Windows ভার্চুয়ালাইজেশনের গভীরতা (পর্ব ১) — আপনার Windows আসলে কোথায় চলছে? হাইপারভাইজার ও পার্টিশন
Hyper-V চালু করলে হোস্ট Windows নিজেই রুট পার্টিশন হিসেবে হাইপারভাইজারের উপর চলে। এই নিবন্ধ VT-x, SLAT ও VMBus-এর ভূমিকা দিয়ে ভার্চুয়াল...
সম্পর্কিত বিষয়
এই পৃষ্ঠাগুলো বিষয়টিকে সেবা ও সিদ্ধান্তের বৃহত্তর প্রেক্ষাপটে স্থাপন করে।
Windows-এর প্রযুক্তিগত বিষয়
Windows ডেভেলপমেন্ট, বাগ তদন্ত ও বিদ্যমান সম্পদ ব্যবহারের প্রবেশদ্বার।
প্রায়শ জিজ্ঞাসিত প্রশ্ন
এই নিবন্ধের বিষয়ে পরামর্শে প্রায়ই আসা প্রশ্ন।
- Ada-র generic কী?
- Type, value, subprogram, এমনকি package নিজেকেই formal parameter হিসেবে নেয়, আর new দিয়ে instantiation-এর মুহূর্তে statically type-check হয় — এমন এক reuse ব্যবস্থা। সাধারণ text substitution নয়, compile-time-এ «এই অংশ এই contract পূরণ করে কি না» স্থির করে। Generic subprogram শুধু declare করলেই ডাকা যায় না; নির্দিষ্ট type দিয়ে instantiate করলে তবেই সাধারণ procedure বা function হিসেবে ব্যবহার করা যায়।
- Ada-র generic আর C++ template-এর ফারাক কী?
- Ada শুরু থেকেই চুক্তি স্পষ্ট করে লেখা contract model ব্যবহার করে। Generic-এর body শুধু formal parameter যে operation-এর প্রতিশ্রুতি দিয়েছে সেগুলো দিয়ে লেখা হয়, আর body একাই type-check হয়। C++ template historically instantiation-এর আগে error দেখায় না; C++20-এর concepts সেটা উন্নত করেছে। Ada-তে value parameter ও subprogram parameter ছাড়াও package নিজেকে formal parameter করা যায়, তাই generic অংশগুলোকে একে অপরের সঙ্গে জোড়া যায়।
- Ada-র formal type parameter-এ type category কেন নির্দিষ্ট করেন?
- Body-তে যে operation ব্যবহার করা যাবে তা contract হিসেবে স্পষ্ট করতে। type T is private হলে assignment বা equality-র মতো মৌলিক operation ছাড়া আর কিছু ধরে নেওয়া যায় না; ছোট-বড় তুলনা বা arithmetic চলে না। Integer arithmetic লাগলে range <>, floating-point লাগলে digits <>, bitwise লাগলে mod <> — এভাবে category দেন। Category নির্দিষ্টকরণ পাঠককেও «এই অংশ কী চায়» বলে দেওয়া type-level specification হিসেবে কাজ করে।
- Ada generic-এ কী কী খেয়াল রাখবেন?
- প্রতিটি type-এর জন্য আলাদা instance তৈরি হয় বলে instance বেশি হলে code size বাড়তে পারে। C++ template ও Rust-এর monomorphization-এও এই trade-off দেখা যায়। Generic package-এর specification-এ ঘোষিত exception প্রতিটি instance-এ আলাদা exception হয়ে যায়। Formal parameter প্রথমে ন্যূনতম রাখুন, বদলাবার কারণ দেখা দিলে বাড়ান; argument বেশি হলে instantiation-এ named association ব্যবহার করুন — এগুলোই বাস্তব নির্দেশিকা।