জানুন কিভাবে জন ম্যাকার্থির প্রতীকী দৃষ্টিভঙ্গি ও লিস্পের নকশা—লিস্ট, রিকার্সন, গারবেজ কালেকশন—এআই ও আধুনিক প্রোগ্রামিংকে প্রভাবিত করেছে।

এটা কোনো “পুরনো AI” জাদুঘরের প্রদর্শনী নয়। এটি সফটওয়্যার নির্মাণাকারীদের — প্রোগ্রামার, টেক লিড, এবং প্রোডাক্ট নির্মাতাদের — জন্য একটি ব্যবহারিক ইতিহাসের পাঠ, কারণ জন ম্যাকার্থির ধারণাগুলো আমাদেরকে প্রোগ্রামিং ভাষার উদ্দেশ্য সম্পর্কে ভাবতে শেখায়।
লিস্প শুধুমাত্র নতুন সিনট্যাক্স ছিল না। এটি এমন বিশ্বাস ছিল যে সফটওয়্যার ধারণাগুলো (শুধু সংখ্যাই নয়) পরিচালনা করতে পারে এবং ভাষা ডিজাইনের পছন্দগুলো গবেষণা, প্রোডাক্ট আইটারে এবং পুরো টুলিং ইকোসিস্টেমকে দ্রুত করে তুলতে পারে।
ম্যাকার্থির উত্তরাধিকারকে পড়ার একটি কার্যকরি উপায় হলো একটি প্রশ্ন যা আজও গুরুত্বপূর্ণ: কতটা সরাসরি আমরা অভিপ্রায়কে একটি কার্যকর সিস্টেমে পড়াতে পারি—বিনা অতিরিক্ত বয়লারপ্লেট, ঘর্ষণ, বা দুর্ঘটনাক্রমে জটিলতার ভিড়ে ডুবে না যায়? এই প্রশ্নটি লিস্পের REPL থেকে আধুনিক “চ্যাট-টু-অ্যাপ” ওয়ার্কফ্লো পর্যন্ত প্রতিধ্বনিত হয়।
জন ম্যাকার্থি কেবলমাত্র AI গবেষণার সূচনা করেছেন বলে স্মরণীয় নন; তিনি একটি নির্দিষ্ট ধরনের AI উপর জোর দিয়েছিলেন: এমন সিস্টেম যা ধারণাগুলোকে পরিচালনা করতে পারে, কেবল উত্তর গণনা নয়। 1950-এর দশকের মাঝামাঝি তিনি ডার্টমাউথ সামার রিসার্চ প্রজেক্ট আয়োজন করেন (যেখানে “artificial intelligence” টার্মটি প্রস্তাবিত হয়) এবং পরবর্তীতে MIT ও স্ট্যানফোর্ডে AI কাজকে আকৃত দিয়েছেন। কিন্তু তাঁর সবচেয়ে স্থায়ী অবদান হতে পারে সেই প্রশ্ন যা তিনি বারবার করেছেন: ধারণাকে নিজেই কি একটি প্রোগ্রামে প্রকাশ করা যায়?
প্রাথমিক কম্পিউটিং সাফল্যগুলোর বেশিরভাগই ছিল সংখ্যাসংক্রান্ত: ব্যালিস্টিক টেবিল, ইঞ্জিনিয়ারিং সিমুলেশন, অপ্টিমাইজেশন, এবং স্ট্যাটিস্টিক্স। এসব সমস্যা গাণিতিকভাবে সহজে ফিট করে।
ম্যাকার্থি অন্য কিছু লক্ষ্য করেছিলেন। মানুষের যুক্তি প্রায়শই কাজ করে এমন ধারণাগুলোর সাথে—যেমন “যদি,” “কারণ,” “মোটামুটি,” “কোনো ধরনের,” এবং “এই শর্তগুলো পূরণ করলে সবগুলো”—যেগুলো ভাসমান-পয়েন্ট সংখ্যায় সহজে উপস্থাপিত হয় না।
ম্যাকার্থির পদ্ধতি জ্ঞানকে প্রতীকে (নাম, সম্পর্ক, শ্রেণি) হিসেবে বিবেচনা করেছিল এবং চিন্তাকে প্রতীকগুলোর উপর নিয়ম-ভিত্তিক রূপান্তর হিসেবে দেখেছিল।
উচ্চ-স্তরেরভাবে কল্পনা করলে: সংখ্যামূলক পদ্ধতি জিজ্ঞাসা করে “কতটা?” আর প্রতীকী পদ্ধতি চেষ্টা করে উত্তর দিতে “এটা কী?” এবং “আমাদের জানা থেকে কি অনুসরণ করে?”
একবার আপনি বিশ্বাস করবেন যে যুক্তি প্রোগ্রামযোগ্য করা যায়, তখন আপনাকে একটি ভাষা দরকার যা নিয়ম, যৌক্তিক বিবৃতি, এবং নেস্টেড সম্পর্কগুলোর মতো এক্সপ্রেশনগুলো আরামদায়কভাবে উপস্থাপন করতে পারে—এবং সেগুলো প্রক্রিয়াকরণ করতে পারে।
লিস্প সেই লক্ষ্য পূরণের জন্য নির্মিত। কাঁচা ধারণাগুলোকে কড়া, পূর্ব-নির্ধারিত ডাটা স্ট্রাকচারের মধ্যে জবরদস্তি করার বদলে, লিস্প কোড ও জ্ঞানকে একই ধরনের আকারে উপস্থাপন করতে স্বাভাবিক করে তুলেছিল। সেই পছন্দটি কেবল শৈল্পিক ছিল না—এটি “একটি চিন্তা বর্ণনা করা” এবং “একটি প্রক্রিয়া চালানো”—এই দুইয়ের মধ্যে একটি ব্যবহারিক সেতু, যা ম্যাকার্থি চাইতেন AI-কে অতিক্রম করাতে।
ম্যাকার্থি ও প্রারম্ভিক AI গবেষকরা যখন “symbolic” বলতেন, তারা রহস্যময় গণিত নয় বোঝাতেন। একটি প্রতীক হলো সাধারণত একটি অর্থবহ লেবেল: একটি নাম যেমন customer, একটি শব্দ যেমন hungry, বা একটি ট্যাগ যেমন IF এবং THEN। প্রতীকগুলো গুরুত্বপূর্ণ কারণ তারা একটি প্রোগ্রামকে ধারণাগুলোর (শ্রেণি, সম্পর্ক, নিয়ম) সাথে কাজ করার সুযোগ দেয় কেবল কাঁচা সংখ্যার বদলে।
সহজভাবে: স্প্রেডশীট দুর্দান্ত যখন আপনার বিশ্ব কলাম ও হিসাবঘটিত। প্রতীকী সিস্টেমগুলো দুর্দান্ত যখন আপনার বিশ্ব নিয়ম, শ্রেণীবিভাগ, ব্যতিক্রম, এবং গঠন দিয়ে ভরা।
অনেক প্রোগ্রামে, 42 এবং "age" এর মধ্যে পার্থক্য কেবল ডেটা টাইপ নয়—এটা যে মানটি কি নির্দেশ করে। একটি প্রতীক আপনাকে এমন কিছু দেয় যা আপনি তুলনা, সংরক্ষণ, এবং মিলিয়ে নিতে পারেন বিহীন অর্থ হারানোর।
এটা ‘প্যারিস একটি শহর’ অথবা ‘ব্যাটারি কম হলে চার্জার খুঁজো’ এর মতো জিনিসগুলো উপস্থাপন করা সহজ করে তোলে।
কোন কিছু ব্যবহারিকভাবে প্রতীক দিয়ে করতে, আপনাকে কাঠামো দরকার। লিস্প খুবই সাদাসিধে একটি কাঠামো প্রচলিত করেছিল: লিস্ট। একটি লিস্ট হলো কেবল আইটেমগুলোর একটি ক্রমান্বয়িত দল, এবং সেই আইটেমগুলো নিজেও লিস্ট হতে পারে। এই এক ধারণা দিয়ে আপনি বাক্য, ফর্ম, এবং ট্রি-আকৃতির জ্ঞান উপস্থাপন করতে পারেন।
এখানে একটি ছোট ধারণাগত উদাহরণ (লিস্প-স্টাইল প্রদর্শন):
(sentence (subject robot) (verb needs) (object power))
এটি প্রায় ইংরেজির মতো পড়ে: একটি বাক্য যার subject, verb, এবং object আছে। এটি স্ট্রাকচার্ড হওয়ায়, একটি প্রোগ্রাম (subject robot) তুলে নিতে পারে বা (object power) কোথাও প্রতিস্থাপন করতে পারে।
একবার তথ্য প্রতীকী স্ট্রাকচারে গেলে, ক্লাসিক AI কাজগুলো অ্যাপ্রোচেবল হয়ে ওঠে:
মূল পরিবর্তন হল যে প্রোগ্রাম কেবল হিসাব করছে না; এটি অর্থবহ জ্ঞানের টুকরোগুলো এমনভাবে অনলাইনে পরীক্ষা ও রূপান্তর করছে যেগুলোকে এটি ইন্সপেক্ট ও বদলাতে পারে।
লিস্পের ডিজাইন সিদ্ধান্তগুলো একাডেমিয়ার মধ্যে আটকে থাকেনি। সেগুলো প্রভাব ফেলেছে কিভাবে মানুষ টুল তৈরি করেছে এবং কত দ্রুত তারা ধারণা পরীক্ষা করতে পেরেছে:
এই গুণগুলো এমন ইকোসিস্টেম তৈরি করে যেখানে পরীক্ষা-নিরীক্ষা সস্তা হয়, প্রোটোটাইপগুলো দ্রুত প্রোডাক্টে পরিণত হতে পারে, এবং দলগুলো পরিবর্তনশীল চাহিদার সাথে মানিয়ে নিতে পারে।
লিস্প শুরু হয়েছিল একটি খুব ব্যবহারিক ডিজাইন সমস্যায়: কিভাবে এমন প্রোগ্রাম লিখবেন যা প্রতীকগুলোর সাথে কাজ করতে পারে ঠিক যেমন সংখ্যার সাথে কাজ করে?
ম্যাকার্থি “ভাল ক্যালকুলেটর” বানাতে চেষ্টা করছিলেন না। তিনি এমন একটি ভাষা চেয়েছিলেন যেখানে (is (parent Alice Bob)) এর মতো এক্সপ্রেশনটি (+ 2 3) এর মতো সহজে সংরক্ষণ, পরীক্ষা, বদলানো, এবং যুক্তি করা যায়।
প্রাধান্য ছিল প্রতীকী তথ্য সহজে উপস্থাপন ও বদলানো করা। তা নিয়ে কাজ করতে লিস্ট ও ট্রি-স্ট্রাকচারগুলোর ওপর গুরুত্ব দেওয়া হয়েছিল, কারণ এগুলো মানুষ আগেই ব্যবহার করে থাকে অর্থ প্রকাশের জন্য: বাক্য, যৌক্তিক নিয়ম, নেস্টেড শ্রেণি, এবং সম্পর্ক।
আরও একটি লক্ষ্য ছিল ভাষার কোর ছোট ও কনসিস্টেন্ট রাখা। যখন একটি ভাষার অনেক “বিশেষ কেস” কম থাকে, আপনি কম সময়ে নিয়ম স্মরণ করবেন এবং বেশি সময় ধারণা গঠনে ব্যয় করবেন। লিস্প ছোট কোর বিল্ডিং ব্লকে ভর করেছিল যা বড় বিমূর্ততা গঠন করার সুবিধা দেয়।
একটি মূল অন্তর্দৃষ্টি হলো প্রোগ্রাম ও ডেটা একই ধরনের গঠন ভাগ করতে পারে। সরলভাবে: আপনার ডেটা যদি একটি নেস্টেড লিস্ট হয়, আপনার প্রোগ্রামও নেস্টেড লিস্ট হতে পারে।
এর ফলে আপনি:
লিস্প একটি মানসিকতা প্রচার করে: ভাষাগুলোকে একটি-সাইজ-সবকিছুর জন্য বানাতে হয় না। সেগুলো নির্দিষ্ট সমস্যা ডোমেন—যেমন যুক্তি, সার্চ, এবং জ্ঞান উপস্থাপনা—কে কেন্দ্র করে ডিজাইন করা যেতে পারে এবং তবু বহু দশক ধরে সাধারণ-উদ্দেশ্যের প্রোগ্রামিংকে প্রভাবিত করতে পারে।
S-এক্সপ্রেশন (symbolic expressions-এর সংক্ষিপ্ত) লিস্পের স্বাক্ষর ধারণা: কোড ও ডেটা উভয়কেই নেস্টেড লিস্ট হিসেবে উপস্থাপনের একটি একক, নিয়মিত উপায়।
এক নজরে, একটি S-এক্সপ্রেশন কেবল প্যারেনথেসিসে থাকা আইটেম—কিছু আইটেম অ্যাটম (নাম এবং সংখ্যা), কিছু আইটেম আবার লিস্ট—এর সমন্বয়। “লিস্টের মধ্যে লিস্ট” নিয়মই গুরুত্বপূর্ণ।
অভ্যস্ত কাঠামো থাকার কারণে, লিস্প প্রোগ্রামগুলো একই বিল্ডিং ব্লক দিয়ে গঠিত হয়। একটি ফাংশন কল, একটি কনফিগারেশনের কৌণিক অংশ, এবং প্রোগ্রামের অংশ—all এগুলোই এক তালিকা হিসেবে প্রকাশ করা যায়।
এই সঙ্গতি মুহূর্তেই লভ্য হয়:
আপনি যদি কখনো লিস্প না লেখেন তবুও এই ডিজাইন পাঠটি গুরুত্বপূর্ণ: যখন একটি সিস্টেম এক বা দুইটি পূর্বানুমানীয় ফর্ম থেকে গঠিত, আপনি কম এজ কেস নিয়ে সময় ব্যয় করেন এবং বেশি নির্মাণে ব্যয় করেন।
S-এক্সপ্রেশন ছোট, পাঠযোগ্য অংশগুলো বড় অংশে মিলিয়ে দেয়। আপনার প্রোগ্রাম “কেবল নেস্টেড লিস্ট” হলে, ধারণা মিলানো প্রায়শই একটি এক্সপ্রেশনকে অন্যটির মধ্যে নেস্ট করা অথবা পুনঃব্যবহারযোগ্য অংশগুলো থেকে লিস্ট গঠন করা।
এটি আপনাকে মডিউলার শৈলীর দিকে ঠেলে দেয়: ছোট অপারেশনগুলো লিখুন যা এক কাজ করে, তারপর সেগুলো একত্র করে বড় অভিপ্রায় প্রকাশ করুন।
সত্যিই অসুবিধা হলো অপরিচিতি। অনেক নতুন ব্যবহারকারীর কাছে বন্ধনীগুলো অদ্ভুত লাগে।
কিন্তু সুফল হলো পূর্বানুমানযোগ্যতা: একবার আপনি নেস্টিং নিয়ম বুঝে গেলেন, প্রোগ্রামের গঠন নির্ভরযোগ্যভাবে দেখা যায়—এবং টুলগুলোও সহজেই এই গঠন ধরে কাজ করতে পারে। সেই স্পষ্টতা লিস্পকে নিজ ভূখণ্ডের বাইরে বড় প্রভাব ফেলতে পরিণত করেছে।
রিকার্সন বোঝার সহজ উপমা: একটি গুলজায়মান ঘর পরিষ্কার করা—আপনি সবকিছু একসাথে সমাধান করেন না। আপনি ছোট ছোট ‘ঘর’ বানান, প্রতিটি আইটেম নিয়ে তার জায়গায় রাখেন, এবং বাকি অংশে একই নিয়ম প্রয়োগ করেন। ধাপগুলো সহজ; শক্তি আসে বারবার প্রয়োগ করার মাধ্যমে যতক্ষণ না কিছুই বাকি থাকে।
লিস্প এই ধারণাটিতে ঝোঁক রাখে কারণ তার ডাটা প্রায়শই লিস্ট থেকে গঠিত: একটি লিস্টের “প্রথম জিনিস” ও “বাকী” থাকে। সেই আকৃতি রিকার্সিভ চিন্তার সাথে নিখুঁতভাবে মেলে।
একটি লিস্ট প্রক্রিয়াজাত করতে, আপনি প্রথম উপাদানটি হ্যান্ডেল করেন, তারপর একই লজিক বাকীর উপরে প্রয়োগ করেন। লিস্ট খালি হলে থামুন—এটাই সেই পরিষ্কার “কিছুই বাকি নেই” মুহূর্ত যা রিকার্সনকে রহস্যময় ন করে তোলে।
ধরুন আপনি সংখ্যার একটি লিস্টের মোট চান।
এটাই। সংজ্ঞাটি সাধারণ ইংরেজির মতো পড়ে, এবং প্রোগ্রামের গঠন ধারনাটিরই প্রতিফলন।
প্রতীকী AI প্রায়শই এক্সপ্রেশনগুলোকে ট্রি-স্ট্রাকচার হিসেবে উপস্থাপন করে (একটি অপারেটর ও উপ-এক্সপ্রেশন)। রিকার্সন হল সেই ট্রি “হাঁটানোর” প্রাকৃতিক উপায়: বাম অংশ একইভাবে মূল্যায়ন করুন যেমন আপনি ডান অংশ মূল্যায়ন করেন, এবং চলতে থাকুন যতক্ষণ না একটি সহজ মানে পৌঁছান।
এই প্যাটার্নগুলো পরবর্তীতে ফাংশনাল প্রোগ্রামিংকে আকার দেয়: ছোট ফাংশন, স্পষ্ট বেস কেস, এবং ডাটা রূপান্তর যা সহজে বোঝা যায়। লিস্পের বাইরেও, “এক ধাপ করো, তারপর বাকীর উপর পুনরাবৃত্তি করো” ধারণাটি ক্লিনার কোড এবং কম সাইড-ইফেক্ট দেয়।
শুরুতে প্রোগ্রামারদের মেমরি ম্যানেজ করতে হতো হাতে: অ্যলোকেট করা, কে ‘মালিক’ তা ট্র্যাক করা, এবং ঠিক সময়ে ফ্রি করা মনে রাখা। সেই কাজ কেবল ডেভেলপমেন্ট ধীর করে না—এটি এমন বাগ তৈরি করে যা পুনরুত্পাদন করা কঠিন এবং পাঠ্যযোগ্য: লিক যা ধীরে ধীরে পারফরম্যান্স কমায়, এবং ড্যাংলিং পয়েন্টার যা দীর্ঘ সময় পর প্রোগ্রাম ক্র্যাশ করে।
জন ম্যাকার্থি লিস্পের জন্য গারবেজ কালেকশন প্রবর্তন করেছিলেন যাতে প্রোগ্রামাররা অর্থ নিয়ে কাজ করতে পারেন, বই-নথিপত্র-রক্ষণাবেক্ষণ নিয়ে না।
উপরিল্লিখিতভাবে, গারবেজ কালেকশন স্বয়ংক্রিয়ভাবে সেই মেমরি খুঁজে বের করে যা রানিং প্রোগ্রাম থেকে আর পৌঁছনীয় নয়—মানে এমন মান যেটা আর কখনও ব্যবহার হবে না—এবং সেই স্পেস পুনঃপ্রাপ্ত করে।
আপনি যদি প্রতিটি অবজেক্ট সঠিকভাবে একবার ফ্রি করেছেন কি না তা না ভাবেন; পরিবর্তে জিজ্ঞাসা হবে “এই অবজেক্টটি কি এখনও অ্যাক্সেসযোগ্য?” যদি না হয়, এটি গারবেজ ধরা হবে।
প্রতীকী AI কাজে লিস্প প্রোগ্রামগুলো প্রায়ই অনেক স্বল্প-জীবী লিস্ট, ট্রি, এবং মধ্যবর্তী ফলাফল তৈরি করে। হাতে মেমরি ম্যানেজমেন্ট থাকলে পরীক্ষানিরীক্ষাকে ধৈর্যের লড়াই বানিয়ে দিত।
GC দিন-প্রতি-দিবসের অভিজ্ঞতা বদলে দেয়:
মুখ্য ধারণা হলো একটি ভাষা ফিচারই হতে পারে একটি টীম মাল্টিপ্লায়ার: রহস্যময় কোরাপদের ডিবাগিং বাড়তি ঘণ্টা কমে গেলে প্রকৃত লজিকে উন্নতি করার জন্য বেশি সময় থাকে।
ম্যাকার্থির পছন্দ লিস্পের ভিতরে আটকে থাকেনি। বহু পরবর্তী সিস্টেম GC গ্রহণ করেছে—কারণ ট্রেড-অফ প্রায়শই নিজেকে মিটায়: Java, C#, Python, JavaScript runtimes, এবং Go সবই বড়-স্কেলে ডেভেলপমেন্টকে নিরাপদ ও দ্রুত করতে GC-তে নির্ভর করে—এমনকি পারফরম্যান্সকে গুরুত্ব দিলেও।
লিস্পে, একটি এক্সপ্রেশন হলো একটি কোড টুকরা যা একটি কনসিস্টেন্ট আকারে লেখা (অften একটি লিস্ট)। ইভালুয়েশন কেবলমাত্র সেই এক্সপ্রেশনের মানে এবং ফলাফল কি হবে তা নির্ধারণের প্রক্রিয়া।
উদাহরণস্বরূপ, যখন আপনি লেখেন “এই সংখ্যাগুলো যোগ কর” বা “এই ফাংশনকে এই ইনপুট দিয়ে কল কর”, ইভালুয়েটর কয়েকটি নিয়ম অনুসর করে সেই এক্সপ্রেশনকে একটি ফলাফলে রূপান্তর করে। এটা ভাষার রেফারি—নির্ধারণ করে কী পরবর্তী করা হবে, কোন ক্রমে, এবং কখন থামতে হবে।
ম্যাকার্থির মূল চালটি কেবল নতুন সিনট্যাক্স উদ্ভাবন নয়—তিনি “মানে যন্ত্র” কে ছোট ও নিয়মিত রেখেছিলেন। ইভালুয়েটর যদি কয়েকটি পরিষ্কার নিয়ম থেকে গঠিত হয়, দুইটি ভাল জিনিস ঘটে:
এই সঙ্গতি লিস্পকে প্রতীকী AI-এর জন্য একটি পরীক্ষাগার বানিয়েছে: গবেষকরা নতুন প্রতিনিধিত্ব ও কন্ট্রোল স্ট্রাকচার দ্রুত পরীক্ষা করতে পেরেছেন, কম্পাইলার টিমের উপর অপেক্ষা না করেই।
ম্যাক্রো হলো লিস্পের উপায় আপনাকে পুনরাবৃত্ত কোড আকার অটোমেট করতে দেয়—শুধু পুনরাবৃত্ত মান নয়। যেখানে একটি ফাংশন আপনার গণনা পুনরাবৃত্তি হওয়া থেকে রক্ষা করে, একটি ম্যাক্রো আপনার কোডের স্ট্রাকচার পুনরাবৃত্তি হওয়া থেকে রক্ষা করে—সাধারণ নিদর্শন যেমন “X কর, কিন্তু লগও কর,” বা “নিয়মের জন্য একটি মিনি-ভাষা সংজ্ঞায়িত কর।”
প্রায়োগিক প্রভাব হলো লিস্প নিজেই ভেতর থেকে নতুন সুবিধা গড়ে তুলতে পারে। আধুনিক অনেক টুল এই ধারনাকে অনুসরণ করে—টেমপ্লেট সিস্টেম, কোড জেনারেটর, এবং মেটা-প্রোগ্রামিং ফিচার—কারণ সেগুলো একই লক্ষ্য সমর্থন করে: দ্রুত পরীক্ষা-নিরীক্ষা এবং স্পষ্ট অভিপ্রায়।
যদি আপনি জানতে চান এই মাইন্ডসেট কিভাবে দৈনন্দিন ডেভেলপমেন্টে প্রভাব ফেলেছে, দেখুন /blog/the-repl-and-fast-feedback-loops।
লিস্পের আকর্ষণের বড় অংশ ছিল কেবল ভাষা নয়—এটি কিভাবে আপনি এর সাথে কাজ করেন। লিস্প REPL-কে প্রসারিত করেছিল: Read–Eval–Print Loop। সাধারণ ভাষায়, এটি কম্পিউটারের সাথে কথোপকথনের মতো—আপনি একটি এক্সপ্রেশন টাইপ করেন, সিস্টেম তা চালায়, ফলাফল মুদ্রণ করে, এবং পরের ইনপুটের অপেক্ষা করে।
পরিবর্তে আপনি একটি পুরো প্রোগ্রাম লিখে, কম্পাইল করে, চালিয়ে, তারপর ত্রুটি খোঁজা—আপনি ধাপে ধাপে আইডিয়াগুলো পরীক্ষা করতে পারেন। আপনি একটি ফাংশন ডিফাইন করেন, কয়েকটি ইনপুট দিয়ে টেস্ট করেন, সেটা টুইক করেন, এবং আবার টেস্ট করেন—সব কয় সেকেন্ডের মধ্যে।
এই রিদম অন্বেষণকে উৎসাহ দেয়, যা প্রারম্ভিক AI কাজের জন্য খুবই গুরুত্বপূর্ণ যেখানে প্রায়শই আপনি সঠিক পদ্ধতি আগে থেকেই জানেন না।
দ্রুত ফিডব্যাক বড় বাজেটকে ছোট চেকে পরিণত করে। গবেষণার জন্য, এটি হাইপোথেসিস পরীক্ষা ও মধ্যবর্তী ফলাফল নিরীক্ষা করা সহজ করে তোলে।
প্রোডাক্ট প্রোটোটাইপিং-এ, এটি ইটারেশনের খরচ কমায়: আপনি বাস্তব ডেটা দিয়ে আচরণ যাচাই করতে পারেন দ্রুত, এজ কেসগুলো তাড়াতাড়ি দেখতে পান, এবং দীর্ঘ বিল্ড সাইকেলের অপেক্ষা না করেই ফিচার উন্নত করেন।
এটাই আধুনিক vibe-coding টুলগুলোর আকর্ষণ: সেগুলো মূলত ফিডব্যাক-লুপ কম্প্রেশন। উদাহরণস্বরূপ, Koder.ai একটি চ্যাট ইন্টারফেস (Agent-ভিত্তিক আর্কিটেকচারের সাথে) ব্যবহার করে প্রোডাক্ট অভিপ্রায়কে দ্রুত কাজ করা ওয়েব, ব্যাকেন্ড, বা মোবাইল কোডে পরিণত করে—অften “try → adjust → try again” লুপকে একটি REPL-এর মতো অনুভব করায়।
REPL ধারণাটি আজ দেখতে পাই:
বিভিন্ন টুল; একই নীতি: চিন্তা ও দেখা মধ্যকার দূরত্ব কমাও।
টীমগুলো REPL-সদৃশ ওয়ার্কফ্লো থেকে সবচেয়ে বেশি লাভ পায় যখন তারা অস্পষ্ট চাহিদা অন্বেষণ করছে, ডেটা-ভারী ফিচার বানানো, API ডিজাইন করা, বা জটিল লজিক ডিবাগ করছে। কাজ যদি দ্রুত শেখার সাথে জড়িত হয়—ব্যবহারকারী, ডেটা, বা এজ কেস নিয়ে—তাহলে ইন্টারেক্টিভ ফিডব্যাক বিলাসিতা নয়; এটি একটি গুণগুণ।
লিস্প “জিতেনি” কারণ সব কেউ প্রতিদিন লিস্প ব্যবহার করে—বরং এটি ধারণাগুলো ছড়িয়েছে যা ধীরে ধীরে অনেক ইকোসিস্টেমে স্বাভাবিক হয়ে উঠেছে।
লিস্প যা ডিফল্ট হিসেবে নিয়েছিল—ফাংশনকে মান হিসেবে গ্রহণ, উচ্চ-অর্ডারের অপারেশন, ছোট অংশগুলোকে রচনা করে প্রোগ্রাম গঠনের প্রবণতা—এগুলো এখন ব্যাপকভাবে দেখা যায়। এমনকি লিস্পের মতো না দেখানো ভাষাগুলোও map/filter-শৈলীর রূপান্তর, ইমিউটেবল ডাটা অভ্যাস, এবং রিকার্সিভ চিন্তাভাবনা (ইটারেটর বা fold-এর মাধ্যমে) উৎসাহ দেয়।
কী পরিবর্তন হল মানসিকতা: ডাটা রূপান্তরগুলোকে পাইপলাইন হিসেবে বিবেচনা করুন, এবং আচরণকে এমন কিছু হিসেবে দেখুন যা আপনি পাস করে প্রবাহে রাখতে পারেন।
লিস্প প্রোগ্রামগুলোকে ডেটা হিসেবে উপস্থাপন করা সহজ করেছিল। সেই মানসিকতা আজ দেখা যায় কিভাবে আমরা AST (abstract syntax trees) দিয়ে কম্পাইলার, ফরম্যাটার, লিন্টার, এবং কোড জেনারেটর তৈরি করি। যখন আপনি AST নিয়ে কাজ করেছেন, আপনি “কোড-অ্যা-ডাটা” নামক একটি কাছের আত্মীয় নিয়ে কাজ করছেন, যদিও স্ট্রাকচারগুলো JSON অবজেক্ট, টাইপড নোড, বা বাইটকোড গ্রাফ হতে পারে।
একই প্রতীকী পদ্ধতি ব্যবহারিক অটোমেশন চালায়: কনফিগ ফরম্যাট, টেমপ্লেটিং সিস্টেম, এবং বিল্ড পাইপলাইনগুলো সবই এমন স্ট্রাকচার্ড উপস্থাপনার উপর নির্ভর করে যা টুলগুলো পরীক্ষা, রূপান্তর, এবং ভ্যালিডেট করতে পারে।
আধুনিক লিস্প-ফ্যামিলি ভাষা (প্রস্তুত ভাবেই: সমসাময়িক লিস্প ও লিস্প-প্রেরিত টুল) ইন্টারনাল DSL কিভাবে ডিজাইন করা হয় তা প্রভাবিত করে—টেস্ট, ডিপ্লয়মেন্ট, ডাটা র্যাঙ্গলিং, বা UI-এর জন্য ছোট, ফোকাসড মিনি-ভাষা।
লিস্পের বাইরে, ম্যাক্রো সিস্টেম, মেটা-প্রোগ্রামিং লাইব্রেরি, এবং কোড জেনারেশন ফ্রেমওয়ার্ক একই ফলাফল লক্ষ্য করে: সমস্যা মেটাতে ভাষা প্রসারিত করা।
একটি বাস্তববাদী পাঠ: সিনট্যাক্স পছন্দ বদলায়, কিন্তু স্থায়ী ধারণাগুলো—প্রতীকী স্ট্রাকচার, রচনাযোগ্য ফাংশন, এবং প্রসার্যযোগ্যতা—দীর্ঘকাল ধরে উপকার দেয়।
লিস্পের খ্যাতি “দক্ষ” থেকে “অপঠনীয়” পর্যন্ত দুলতে পারে, প্রায়শই রসিদ-সাক্ষী বর্ণনা নয় বরং বাস্তব অভিজ্ঞতা থেকে নয়। সত্যি হলো: লিস্প কিছু পছন্দ করে যা নির্দিষ্ট সেটিংয়ে শক্তিশালী এবং অন্যখানে অপ্রয়োজনীয়।
নতুনদের কাছে লিস্পের ইউনিফর্ম সিনট্যাক্স প্রোগ্রামের “ভিতরের” মতো দেখতে পারে, না যে একটি পোলিশ-কাঠামোর পৃষ্ঠ। সেই অস্বস্তি বাস্তব, বিশেষত যদি আপনি এমন ভাষার অভ্যাসে থাকেন যেখানে সিনট্যাক্স ভিন্ন কনস্ট্রাক্ট আলাদা করে।
তবে ঐতিহাসিকভাবে, লিস্পের গঠনই মূল লক্ষ্য: কোড ও ডাটা একই আকার ভাগ করে, ফলে প্রোগ্রামগুলো রূপান্তর, জেনারেট ও বিশ্লেষণে সহজ হয়। ভাল এডিটর সাপোর্ট (ইন্ডেন্টেশন, স্ট্রাকচারাল নেভিগেশন) থাকলে লিস্প কোড প্রায়ই আকারের মাধ্যমে পড়া যায়, কেবল বন্ধনীর গণনা নয়।
একটি প্রচলিত স্টেরিওটাইপ হলো লিস্প ধীর। ইতিহাসগতভাবে কিছু ইমপ্লিমেন্টেশন নিম্ন-লেভেল ভাষার তুলনায় বেশি কষ্ট পেয়েছে, এবং ডায়নামিক ফিচারগুলো অতিরিক্ত ওভারহেড যোগ করতে পারে।
কিন্তু “লিস্প” কে একক পারফরম্যান্স প্রোফাইলে বেঁধে দেওয়াটা সঠিক নয়। অনেক লিস্প সিস্টেম দীর্ঘদিন ধরে কম্পাইলেশন, টাইপ ডিক্লারেশন, এবং গুরুতর অপ্টিমাইজেশন সমর্থন করে। বাস্তবতাটি হলো: আপনার কতোটা মেমরি লেআউট, পূর্বানুমানযোগ্য ল্যাটেন্সি, বা কাঁচা থ্রুপুট নিয়ন্ত্রণ দরকার—এবং আপনার লিস্প ইমপ্লিমেন্টেশন সেই চাহিদা লক্ষ্য করে কি না তা বিচার করা উচিত।
আরেকটি যুক্তিসঙ্গত সমালোচনা হলো ইকোসিস্টেম ফিট। লিস্প ডায়ালেক্ট ও ডোমেইনের উপর নির্ভর করে লাইব্রেরি, টুলিং, এবং ট্যালেন্ট পুল মেইনস্ট্রিম স্ট্যাকের তুলনায় ছোট হতে পারে। যদি আপনি দ্রুত শিপ করতে চান বড় টিম নিয়ে, এটা ভাষার সৌন্দর্যের চেয়েও বেশি গুরুত্বপূর্ণ হতে পারে।
লিস্পকে সেকেলের ভিত্তিতে বিচার করার বদলে, এর মৌলিক ধারণাগুলো আলাদাভাবে মূল্যায়ন করুন: ইউনিফর্ম স্ট্রাকচার, ইন্টারেক্টিভ ডেভেলপমেন্ট, এবং ভাষার ভিতর থেকে প্রসার্যতা গড়ার ব্যবস্থা। আপনাকেও না-লিস্প-শিপ করলেও এই ধারণাগুলো যেকোনো ভাষায় কিভাবে কোড লিখবেন তা ধারালো করতে পারে।
ম্যাকার্থি কেবল একটি ঐতিহাসিক ভাষাই ছেড়ে যাননি—তিনি এমন অভ্যাসগুলোর একটি সেট দিয়েছেন যা এখনও সফটওয়্যারকে পরিবর্তন, ব্যাখ্যা, এবং প্রসারিত করা সহজ করে।
চতুর পৃষ্ঠভূমির চেয়ে সরল কোর পছন্দ করুন। অল্প সংখ্যক অর্থোপযোগী বিল্ডিং ব্লক শেখার সহজ এবং ভাঙা কঠিন।
ডাটা শেপগুলো একরূপ রাখুন। বহু জিনিস একই প্রতিনিধিত্ব ভাগ করলে টুলিং সহজ হয়: প্রিন্টার, ডিবাগার, সিরিয়ালাইজার, ট্রান্সফর্মার এগুলো পুনঃব্যবহারযোগ্য হয়।
প্রোগ্রামকে ডাটা হিসেবে বিবেচনা করুন (এবং ডাটাকে প্রোগ্রাম হিসেবে) যেখানে সাহায্য করে। যদি আপনি স্ট্রাকচারগুলো ইন্সপেক্ট ও ট্রান্সফর্ম করতে পারেন, আপনি নিরাপদ রিফ্যাক্টর, মাইগ্রেশন, এবং কোড জেনারেটর বানাতে পারেন।
কোথাও বিরক্তিকর কাজ অটোমেট করুন। গারবেজ কালেকশন ক্লাসিক উদাহরণ, কিন্তু বিস্তৃত পয়েন্ট হলো: এমন অটোমেশন এতে বিনিয়োগ করুন যা পুরো শ্রেণির ভুল প্রতিরোধ করে।
ফিডব্যাক লুপের জন্য অপ্টিমাইজ করুন। ইন্টারেক্টিভ ইভালুয়েশন (REPL-স্টাইল) ছোট পরীক্ষা, দ্রুত যাচাই, এবং আচরণ সম্পর্কে উন্নত অন্তর্যাম দেয়।
প্রসার্যকে প্রথম-শ্রেণীর ডিজাইন লক্ষ্য করুন। লিস্প ম্যাক্রো একটি উত্তর; অন্য পরিবেশে এটি প্লাগইন, টেমপ্লেট, DSL, বা কম্পাইল-টাইম রূপান্তর হতে পারে।
লাইব্রেরি বা আর্কিটেকচার বেছে নেওয়ার আগে একটি বাস্তব ফিচার—ধরুন “ডিসকাউন্ট নিয়ম”—নিন এবং এটিকে:
এই অনুশীলন প্রায়শই দেখায় কোথায় ‘কোড-অ্যা-ডাটা’ প্যাটার্ন, রুল ইঞ্জিন, বা DSL-আধারিত কনফিগ সিস্টেম সুবিধা দেবে।
লিস্প না থাকলেও আপনি মাইন্ডসেটটি গ্রহণ করতে পারেন: AST-ধাঁচ উপস্থাপন তৈরি করুন, পুনরাবৃত্ত গ্লু-চালাকির জন্য কোড জেনারেশন ব্যবহার করুন, এবং ডাটা-ফার্স্ট পাইপলাইন (parse → transform → evaluate) স্ট্যান্ডার্ডাইজ করুন। অনেক টীম মধ্যবর্তী প্রতিনিধিত্বগুলো স্পষ্ট করে এই সুবিধা পায়।
যদি আপনি REPL নীতি পছন্দ করেন কিন্তু মেইনস্ট্রিম স্ট্যাকে প্রোডাক্ট ফিচার শিপ করছেন, আপনি টুলিং থেকে চিন্তাধারাটা ধার নিতে পারেন: টাইট ইটারেশন লুপ, স্ন্যাপশট/রোলব্যাক, এবং এক্সিকিউশনের আগে স্পষ্ট পরিকল্পনা। উদাহরণস্বরূপ, Koder.ai-তে প্ল্যানিং মোড, স্ন্যাপশট এবং রোলব্যাক আছে যাতে দ্রুত ইটারেশন নিরাপদ থাকে—এর অপারেশনাল echo লিস্পের “দ্রুত পরিবর্তন, কিন্তু নিয়ন্ত্রণে থাক” নীতির সাথে সম্পর্কিত।
ম্যাকার্থির স্থায়ী প্রভাব হলো: যখন আমরা যুক্তিকেই প্রোগ্রামযোগ্য করি এবং ধারণা থেকে কার্যকর সিস্টেমে যাওয়ার পথকে যতটা সম্ভব সরাসরি রাখি, তখন প্রোগ্রামিং আরো শক্তিশালী হয়।
প্রতীকী চিন্তা সরাসরি ধারণা ও সম্পর্কগুলোকে উপস্থাপন করে (যেমন: “customer”, “is-a”, “depends-on”, “if…then…”), এবং তারপর সেই উপস্থাপনাগুলোর উপর নিয়ম ও রূপান্তর প্রয়োগ করে।
এটি সবচেয়ে উপকারী যখন আপনার সমস্যা পূর্ণ হয়েছে গঠন, ব্যতিক্রম, এবং অর্থ দিয়ে (নিয়ম ইঞ্জিন, পরিকল্পনা, কম্পাইলার, কনফিগারেশন, ওয়ার্কফ্লো লজিক), কেবল অঙ্কগত হিসাব নয়।
ম্যাকার্থি জোর দিয়েছিলেন যে তর্ক-চিন্তা (reasoning) প্রোগ্রাম হিসেবে প্রকাশ করা যায়—শুধু হিসাব করা নয়।
এই দৃষ্টিভঙ্গি যা গঠন করেছে:
লিস্ট হচ্ছে “পার্থে গঠিত জিনিস” উপস্থাপনের একটি ক্ষুদ্র, নমনীয় উপায়। কারণ তালিকার উপাদানগুলিও নিজে তালিকা হতে পারে, আপনি স্বয়ংক্রিয়ভাবে ট্রি-স্ট্রাকচার পেয়ে যান।
এটি সহজ করে তোলে:
S-এক্সপ্রেশন আপনাকে দেয় একটি একক ইউনিফর্ম আকার—নেস্টেড লিস্ট হিসেবে কোড ও ডাটা উভয়ের জন্য।
এই একরূপতা সাধারণত সিস্টেমগুলোকে সহজ করে তোলে কারণ:
ম্যাক্রো পুনরাবৃত্ত কোড কাঠামো (code structure) স্বয়ংক্রিয় করে—শুধু পুনরাবৃত্ত গণনা নয়।
ম্যাক্রো ব্যবহার করুন যখন আপনি চান:
যদি কেবল পুনরায় ব্যবহারযোগ্য লজিক দরকার হয়, তাহলে সাধারণত একটি ফাংশনই ভালো বিকল্প।
গারবেজ কালেকশন (GC) স্বয়ংক্রিয়ভাবে সেই মেমরি ফিরিয়ে দেয় যা রানিং প্রোগ্রামের থেকে আর পৌঁছনীয় নয়—এবং ফলে এক ধরণের বাগ (ড্যাংলিং পয়েন্টার, ডাবল ফ্রি) কমে যায়।
এটি বিশেষভাবে উপকারী যখন আপনার প্রোগ্রাম অনেক স্বল্প-জীবী স্ট্রাকচার (লিস্ট/ট্রি/AST) তৈরি করে, কারণ আপনি প্রোটোটাইপ ও রিফ্যাক্টর করতে পারবেন কোনো হাতে-কলমে মেমরি স্ট্র্যাটেজি না নিয়ে।
REPL “চিন্তা → চেষ্টা → পর্যবেক্ষণ” লুপকে ছোট করে দেয়। আপনি একটি ফাংশন ডিফাইন করে তা চালাতে, তা টুইক করে আবার চালাতে পারেন—সবই তৎক্ষণাৎ।
একই সুবিধা অন্য স্ট্যাকে পেতে:
সংক্রান্ত পড়তে পারেন: /blog/the-repl-and-fast-feedback-loops
অনেক বর্তমান ওয়ার্কফ্লো একই মূল চিন্তা ব্যবহার করে:
map/filter, কম্পোজিশন)আপনি হয়তো কখনো লিস্প শিপ না করলেও প্রতিদিনই লিস্প-উৎপত্তির অভ্যাস ব্যবহার করেন।
বস্তুনিষ্ঠ ট্রেড-অফগুলো:
প্রযোজ্য উপায় হচ্ছে: সেকেলের বদলে মৌলিক ধারণাগুলো মূল্যায়ন করুন—ইউনিফর্ম স্ট্রাকচার, ইন্টারেক্টিভ ডেভ, এবং ভাষার ভিতর থেকে প্রসার করার সক্ষমতা।
একটি ছোট অনুশীলন:
এটি প্রায়ই দেখায় কোথায় “কোড-অ্যা-ডাটা” প্যাটার্ন, নিয়ম ইঞ্জিন, বা DSL-স্টাইল কনফিগ সহজ করবে।