কম্পিউটার টিউটোরিয়াল

রুবিতে টাইপ চেকিং освоয়ন: সাধারণ রানটাইম এরর এড়াতে

এই পোস্টটি একটি মজার অনুমান গেম দিয়ে শুরু করি: আপনি কী মনে করেন AppSignal-এ রুবি অ্যাপ্লিকেশনে ট্র্যাক হওয়া সবচেয়ে কমন এরর কী?

অনেকেই উত্তর দেবেন NoMethodError, একটি এক্সেপশন যা একটি অবজেক্টে অস্তিত্বহীন মেথড কল করার কারণে হয়। কখনও কখনও এটি টাইপো হতে পারে মেথড নামে, কিন্তু অধিকাংশसमয় এটি ভুল টাইপের অবজেক্টে মেথড কল করার ফলাফল, যা অনেক সময় অপ্রত্যাশিত nil হয়। রুবি ডেভেলপাররা এমন এরর কমাতে কি কিছু করতে পারেন?

টাইপস থেকে রক্ষা?

টেক্স्ट এডিটর বা প্রোগ্রামিং ল্যাঙ্গুয়েজের পছন্দ বাদে, টাইপ সিস্টেম সম্পর্কে আলোচনা থেকে দ্রুত হিটেড ডিবেট হতে পারে এমন বিষয় কম। আমরা এখানে বিস্তারিতে যাব না, কিন্তু Криস স্মিথের পোস্ট "What To Know Before Debating Type Systems" এতে চমৎকার কাজ করেছে।

সর্বব্যাপী терমে, টাইপ সিস্টেম দুটি প্রধান ক্যাটাগরিতে ভাগ করা যায়—স্ট্যাটিক এবং ডাইনামিক।astomatonormal, প্রথমটি time ahead-এ হয় (কম্পাইলার বা একটি আলাদা টুল দিয়ে), ডাইনামিক টাইপ চেকিং রানটাইমে হয়, যেখানে এটি এক্সেপশনে লিড করতে পারে যদি এক्चুয়াল টাইপ ডেভেলপারদের এক্সপেক্টেশনের সাথে অ্যালাইন না করে। উভয় ফিলোসফির সমর্থকরা ধারাবাহিক মতামত রাখেন, কিন্তু অহങ്കারও আছে অনেক মিসকনসেপশন: স্ট্যাটিক টাইপিং এর মানে কোॅপিয়াস টাইপ অ্যানোতেশন নয়—অনেক আধুনিক কম্পাইলার টাইপগুলো নিজে বের করতে পারে, একটি প্রক্রিয়া যাকে "টাইপ ইনফারেন্স" বলা হয়। অন্যদিকে, ডাইনামিক্যালি টাইপড ল্যাঙ্গুয়েজগুলো সিগনিফিক্যান্টলি হাই ডিফেক্ট রেট প্রদর্শন করে না তাদের স্ট্যাটিক্যালি টাইপড কাউন্টারপার্টসের চেয়ে।

ডাক টাইপিং

রুবি নিজে একটি ডাইনামিক্যালি টাইপ-চেকড ল্যাঙ্গুয়েজ এবং "ডাক টাইপিং" অপ্রোচ ফলো করে:

যদি সেটি হাঁসের মত হাঁটে এবং হাঁসের মত কুয়াক করে, তবে সেটি অবশ্যই হাঁস।

এর মানে হল রুবি ডেভেলপাররা সাধারণত একটি অবজেক্টের টাইপ নিয়ে বেশি চিন্তা করেন না, বরং সেটি কিছু নির্দিষ্ট "মেসেজ" (অথবা মেথড) রেসপন্ড করে কিনা সেটি নিয়ে চিন্তা করেন। তাহলে রুবিতে স্ট্যাটিক টাইপিং কেন ভাববেন, আপনি জিজ্ঞাসা করতে পারেন? যদিও এটি কনീ প্যানেসিয়া নয় যা আপনার কোড ম্যাজিক্যালি বাগ-ফ্রি করে দেবে, এটি কিছু সুবিধা প্রদান করে:
  • Correctness: স্ট্যাটিক টাইপিং কিছু নির্দিষ্ট ক্লাস বাগ প্রিভেন্ট করতে ভালো, যেন 위에서 বোঝানো NoMethodError।
  • Tooling: çox সময়, ডেভেলপমেন্টের সময় স্ট্যাটিক টাইপ ইনফরমেশন থাকলে ভালো টুলিং অপশন থাকে (যেন IDE-এ রিফ্যাক্টরিং সাপোর্ট, ইত্যাদি)।
  • Documentation: অনেক স্ট্যাটিক্যালি টাইপড ল্যাঙ্গুয়েজের চমৎকার বিল্ট-ইন ডকুমেন্টেশন টুল থাকে। Haskell-এর Hoogle এটি গরেট ইফেক্টে ব্যবহার করে একটি সার্চ ইঞ্জিন অফার করে যেটি ফাংশন লুকআপ করে তাদের টাইপ সিগনেচার দিয়ে।
  • Performance: কম্পাইলার যত বেশি ইনফরমেশন পাবে, তত বেশি পারফরম্যান্স অপ্টিমাইজেশন পোটেনশিয়ালি অ্যাপ্লাই করা যায়।
এই লিস্ট এক্সহাস্টিভ নয়, এবং অধিকাংশ পয়েন্টের জন্য কাউন্টারএকজাম্পল পাওয়া যায়, তবে এতে নিশ্চিত একটি কোর অফ ট্রুথ আছে।

গ্র্যাজুয়াল টাইপ চেকিং

সাম্প্রতিক বছরে একটি অপ্রোচ যাকে সাধারণত "গ্র্যাজুয়াল টাইপ চেকিং" বলা হয়, বিভিন্ন ডাইনামিক্যালি টাইপ-চেকড ল্যাঙ্গুয়েজে গুছিয়ে এসেছে: JS-এর জন্য TypeScript, PHP-এর জন্য Hack, এবং Python-এর জন্য mypy। এই অপ্রোচগুলোতে যা কমন হল সেগুলো অল-অর-নথিং অপ্রোচ ফলো করে না, বরং ডেভেলপারদের আস্তে আস্তে টাইপ ইনফরমেশন ভেরিয়েবল এবং এক্সপ্রেশনে যোগ করতে দেয় যেন তারা ফিট বোঝায়। এটি এক্সিস্টিং বড় কোডবেসের জন্য খুব ইউজফুল, যেখানে সিস্টেমের সবচেয়ে ক্রিটিক্যাল অংশগুলো স্ট্যাটিক্যালি চেক করা যায় বাকি পার্ট अनটাইপড এবং রানটাইমে চেকড রেখে। রুবির জন্য সকল টাইপ চেকিং সমাধান যেগুলো আমরা এই আর্টিকেলের বাকি অংশে এক্সপ্লোর করব, সেগুলো একই অপ্রোচ ফলো করে।

অপশনসমূহ

রুবি ডেভেলপাররা কেন স্ট্যাটিক টাইপ চেকিং তাদের ডেভেলপমেন্ট ওয়ার্কফ্লোতে যোগ করতে চান সেটি দেখার পর, কিছু বর্তমান জনপ্রিয় অপশন এক্সপ্লোর করার সময়। তবে, নোট করুন যে রুবিতে স্ট্যাটিক টাইপ চেকিং যোগ করার আইডিয়া নতুন নয়। মেরিল্যান্ড યুনিভার্সিটির রিসার্চাররা Diamondback Ruby (Druby) নামক একটি রুবি এক্সটেনশনে ২০০৯ পর্যন্ত কাজ করছিলেন এবং টাফটস যুনিভার্সিটি প্রোগ্রামিং ল্যাঙ্গুয়েজ গ্রুপ ২০১৩-এ "The Ruby Type Checker" নামক একটি পেপার রিলিজ করেছিল, যা শেষে RDL প্রজেক্টে লিড করেছিল, যা টাইপ চেকিং এবং ডিজাইন-বাই-কন্ট্র্যাক্ট ক্যাপাবিলিটি লাইব্রেরি হিসেবে অফার করে।

Sorbet

Stripe द्वारा विकसित, Sorbet বর্তমানে রুবির জন্য সবচেয়ে বেশি আলোচিত টাইপ চেকিং সমাধান, इसलिये নয় কম বড় কোম্পানিগুলো যেন Shopify, GitLab, Kickstarter এবং Coinbase এর ভেতরে ক্লোজড বিটা ফেজে আডপ্ট করেছিল। এটি আসল সালে রুবি কাইগিতে ঘোষণা করা হয়েছিল এবং এর প্রথম পাবলিক রিলিজ এই বছরের জুন ২০-এ হয়েছিল। Sorbet মডার্ন C++-এ লেখা হয়েছে এবং Matz-এর পছন্দের বিপরীতে (কোট: "I hate type annotations"), এটি টাইপ অ্যানোতেশন-ভিত্তিক অপ্রোচ নেয়া হয়েছে। Sorbet-এর একটি বিশেষভাবে আকর্ষণীয় দিক হল এটি স্ট্যাটিক এবং ডাইনামিক টাইপ চেকিংর কনবিনেশন বেছে নেয়া, যেহেতু রুবির এক্সট্রিমলি ডাইনামিক নেচার এবং মেটা প্রোগ্রামিং ক্যাপাবিলিটি স্ট্যাটিক টাইপ সিস্টেমের জন্য চ্যালেঞ্জিং।

# typed: true class Test extend T::Sig sig {params(x: Integer).returns(String)} def to_s(x) x.to_s end end
টাইপ চেকিং ইনেবল করতে, আমাদের প্রথমে # typed: true ম্যাজিক কমেন্ট যোগ করতে হবে এবং আমাদের ক্লাসকে T::Sig মডিউল দিয়ে এক্সটেন্ড করতে হবে। আসল টাইপ অ্যানোতেশন স্পেসিফাই হয় sig মেথড দিয়ে:
sig {params(x: Integer).returns(String)}
যা স্পেসিফাই করে এই মেথডটি একটি সিঙ্গেল আর্গুমেন্ট নেয় x নামে যা Integer টাইপের এবং রিটার্ন করে একটি String। এই মেথডকে ভুল আর্গুমেন্ট টাইপ দিয়ে কল করলে এরর হবে:
Test.new.to_s("42") # Expected Integer but found String("42") for argument x
এই বেসিক চেকগুলো ছাড়াও, Sorbet-এ আরও অনেক ট্রিক আছে। উদাহরণস্বরূপ, এটি আমাদের নিল-এ NoMethodError থেকে বাঁচাতে পারে:
users = T::Array[User].new user = users.first user.username # Method username does not exist on NilClass component of T.nilable(User)
উপরের স্নিপেট একটি খালি অ্যারে ডিফাইন করে User অবজেক্টের এবং যখন আমরা প্রথম এলিমেন্ট এক্সেস করার চেষ্টা করি (যেটি nil রিটার্ন করবে) Sorbet সঠিকভাবে সতর্ক করে যে username নামে কোনো মেথড NilClass-এ উপলব্ধ নেই। যাইহোক, আপনি যদি নিশ্চিত হন একটি নির্দিষ্ট ভ্যালু কখনো nil হতে পারে না, আপনি T.must ব্যবহার করে Sorbet-কে এটা জানাতে পারেন:
users = T::Array[User].new user = T.must(users.first) user.username
উপরের কোড এখন টাইপ চেক হবে, কিন্তু এটি রানটাইম এক্সেপশনে লিড করতে পারে, তাই এই ফিচার সতর্কতার সাথে ব্যবহার করুন। Sorbet আরও অনেক কিছু করতে পারে আমাদের জন্য: ডেড কোড ডিটেকশন, টাইপ পিনিং (এসেনশিয়ালি একটি ভেরিয়েবলকে একটি নির্দিষ্ট টাইপে কমিট করা, উদাহরণস্বরূপ, একবার স্ট্রিং অ্যাসাইন হলে, এটি কখনো ইন্টিজার অ্যাসাইন করা যাবে না), বা ইন্টারফেস ডিফাইন করার ক্ষমতা। এডিশনালি, Sorbet "Ruby Interface" ফাইল (rbi) এর সাথে কাজ করতে পারে যা সেটি একটি sorbet/ ফোল্ডারে রাখে আপনার কারেন্ট ওয়ার্কিং ডিরেক্টরিতে। এটি আমাদের সব জেমের জন্য ইন্টারফেস ডিফাইনেশন জেনারেট করতে দেয়, যা আরও বেশি টাইপ এরর খুঁজে বের করতে সাহায্য করতে পারে। Sorbet-এর সম্পর্কে এখানেক বেশি আছে যা এক আর্টিকেলে কভার করা সম্ভব নয় (যেন ভিন্ন স্ট্রিক্টনেস লেভেল বা মেটা প্রোগ্রামিং প্লাগইন), কিন্তু এর ডকুমেন্টেশন ইতিমধ্যে খুব ভালো এবং PR-এর জন্য ওপেন।

Steep

Sorbet-এর সবচেয়ে বিখ্যাত অ্যাল্টারনেটিভ হল Soutaro Matsumoto-এর Steep। এটি অ্যানোতেশন ব্যবহার করে না এবং নিজে কোনো টাইপ ইনফারেন্স করে না। বরং, এটি সম্পূর্ণভাবে নির্ভর করে .rbi ফাইলগুলোর ওপর sig ডিরেক্টরিতে। নিচের সিম্পল রুবি ক্লাস থেকে শুরু করি:

class User attr_reader :first_name, :last_name, :address def initialize(first_name, last_name, address) @first_name = first_name @last_name = last_name @address = address end def full_name "#{first_name} #{last_name}" end end
আমরা এখন একটি ইনিশিয়াল user.rbi ফাইল স্ক্যাফফোল্ড করতে পারি নিম্নলিখিত কমান্ড দিয়ে:
$ steep scaffold user.rb > sig/user.rbi
এটি নিম্নলিখিত ফাইল রেজাল্ট করে যা একটি স্টার্টিং পয়েন্ট হিসেবে ইন্ডিকেটেড (ইলাস্ট্রেটেড দিয়ে যে সব টাইপ any হিসেবে স্পেসিফাই করা হয়েছে, যেটি কোনো সেফটি দেয় না):
class User @first_name: any @last_name: any @address: any def initialize: (any, any, any) -> any def full_name: () -> String end 
যাইহোক, আপনি যদি এই পয়েন্টে টাইপ চেক করার চেষ্টা করেন, কিছু এরর এনকাউন্টার করবেন:
$ steep check user.rb:11:7: NoMethodError: type=::User, method=first_name (first_name) user.rb:11:21: NoMethodError: type=::User, method=last_name (last_name) 
এই এররগুলো আসার কারণ হল Steep-এর একটি স্পেশাল কমেন্ট প্রয়োজন যাতে সেটি জানতে পারে কোন মেথডগুলো attr_reader দিয়ে ডিফাইন করা হয়েছে, তাই সেটি যোগ করি:
# @dynamic first_name, last_name, address attr_reader :first_name, :last_name, :address
এডিশনালি, আমাদের জেনারেটেড .rbi ফাইলে মেথডের ডিফিনিশন যোগ করতে হবে। আমরা যাইহোক, সিগনেচারগুলো any-থেকে একচুয়াল টাইপে চেঞ্জ করি:
class User @first_name: String @last_name: String @address: Address def initialize: (String, String, Address) -> any def first_name: () -> String def last_name: () -> String def address: () -> Address def full_name: () -> String end 
এখন, সব কিছু এক্সপেক্টেডলি কাজ করছে এবং steep check কোনো এরর রিটার্ন করছে না। আমরা যা দেখেছি সেটির উপরে, Steep জেনেরিক্স সাপোর্ট করে (যেন Hash) এবং ইউনিয়ন টাইপস, যেটি রিপ্রেজেন্ট করে অনেক টাইপের মধ্যে ইদর-উদর চয়েস। উদাহরণস্বরূপ, একটি ইউজারের top_post মেথড আপনার ইউজার দ্বারা লেখা হাইএস্ট-র‍্যাঙ্কড পোস্ট রিটার্ন করতে পারে, অথবা nil যদি তারা এখনো কন্ট্রিবিউট না করেছে হয়। এটি রিপ্রেজেন্ট করা হয় ইউনিয়ন টাইপ (Post | nil) দিয়ে, এবং সংশ্লিষ্ট সিগনেচার দেখাবে এরকম:
def top_post: () -> (Post | nil) 
Steep নিশ্চিতভাবে Sorbet-এর চেয়ে কম ফিচার دار, কিন্তু এটি এখনও একটি হেল্পফুল টুল এবং মনে হয় Matz টাইপ চেকিং রুবি ৩-এ কেমন দেখাবে সেটির সাথে আরও অ্যালাইন।

Ruby Type Profiler

Yusuke Endoh (রুবি ডেভেলপার সার্কিলে "mame" হিসেবে ভালো পরিচিত) Cookpad-এ কাজ করছেন একটি যাকে লেভেল ১ টাইপ চেকার বলা হয় Ruby Type Profiler। এই আর্টিকেলে প্রজেন্ট করা অন্যান্য সমাধানগুলোর বিপরীতে, এটিকে সিগনেচার ফাইল বা টাইপ অ্যানোতেশনের প্রয়োজন নেই বরং এটি যতটা সম্ভব ইনফার করার চেষ্টা করে একটি রুবি প্রোগ্রাম সম্পর্কে সেটি পার্স করার সময়। যদিও এটি Steep বা Sorbet-এর চেয়ে কম পটেনশিয়াল সমস্যা ক্যাচ করে, এটি ডেভেলপারের কোনো এক্সট্রা কস্ট ছাড়া আসে।

সারাংশ

কেউ ভবিষ্যৎ প্রিডিক্ট করতে পারে না, কিন্তু মনে হয় রুবিতে টাইপ চেকিং এমন কিছু যেটি থাকার জন্য এসেছে। বর্তমানে, একটি "Ruby Signature Language" স্ট্যান্ডার্ডাইজ করার প্রচেষ্টা চলছে .rbi ফাইলগুলোর জন্য ব্যবহার করা (পটেনশিয়ালি Ruby Type Profiler দ্বারা স্ক্যাফফোল্ডেড), যাতে ডেভেলপাররা যেকোনো টুল পছন্দ করলে ব্যবহার করতে পারেন। Steep ইতিমধ্যে লাইব্রেরি অথরদের টাইপ ইনফরমেশন শিপ করতে দেয় তাদের জেমের সাথে, এবং Sorbet-এ একই মেকানিজম আছে sorbet-typed আকারে, যেটি ইনস্পায়ার্ড ছিল DefinitelyTyped রিপোজিটরি থেকে TypeScript ডেফিনিশনের জন্য। আপনি যদি রুবিতে টাইপ চেকিং-এর ভবিষ্যৎ গড়ে তোলতে সাহায্য করতে আগ্রহী হন, এখন একটা গ্রেট টাইম জড়িত হওয়ার!

  1. সঠিক রুবি IDE বেছে নিন: ডেভেলপারদের জন্য একটি প্র্যাক্টিক্যাল গাইড

  2. রুবি কনস্ট্যান্টস: সংজ্ঞা, ব্যবহার এবং সেরা অনুশীলন

  3. PC ধীর হয়ে গেল? হার্ডওয়্যার বদলানোর আগে ১০টি জরুরি চেক

  4. উইন্ডোজ ১০-এ র‍্যাম টাইপ চেক করুন: একটি সহজ গাইড