টাইপ কোয়ার্সন হলো একটি অবজেক্টের টাইপ অন্য টাইপে পরিবর্তন করা, তার মানসহ। উদাহরণস্বরূপ, #to_s দিয়ে ইন্টিজারকে স্ট্রিংয়ে বা #to_i দিয়ে ফ্লোটকে ইন্টিজারে পরিবর্তন করা। কদাচিৎ কম পরিচিত #to_str এবং #to_int মেথড কিছু অবজেক্ট ইমপ্লিমেন্ট করে যা প্রথম দৃষ্টিতে একই কাজ করে বলে মনে হয়, কিন্তু কিছু পার্থক্য আছে।
AppSignal Academy-এর এই সংস্করণে, আমরা রুবিতে এক্সপ্লিসিট কাস্টিং এবং ইম্প্লিসিট কোয়ার্সন গভীরে যাব, এক্টর টাইপকাস্টিং-এর ওপর সামান্য আলোচনা করব। আমরা দুটি পদ্ধতির পার্থক্য কভার করব এবং চর্চা করব কীভাবে সেগুলো ব্যবহৃত হয়।
প্রথমে দেখি রুবিতে আমরা সাধারণত মানগুলো বিভিন্ন টাইপে কীভাবে কোয়ার্সন করি এক্সপ্লিসিট কাস্টিং হেল্পারসহ।
এক্সপ্লিসিট কাস্টিং হেল্পারস
সবচেয়ে সাধারণ কাস্টিং হেল্পার হচ্ছে #to_s, #to_i, #to_a এবং #to_h। এগুলো এক্সপ্লিসিট কাস্টিং মেথড। এগুলো সহজে একটি মানকে এক টাইপ থেকে অন্য টাইপে রূপান্তর করতে সাহায্য করে।
এক্সপ্লিসিট হেল্পারগুলো একটি স্পষ্ট প্রতিশ্রুতি নিয়ে আসে। যখনই কোনো অবজেক্টের ওপর #to_s কল করা হয়, এটি সবসময় একটি স্ট্রিং রিটার্ন করবে, এমনকি যদি অবজেক্টটি ভালোভাবে স্ট্রিংএ কনভার্ট না হয়। এটি মাইকেল কিটনকে ব্যাটম্যান হিসেবে কাস্ট করার মতো। আপনি একটি ব্যাটম্যান পাবেন, এমনকি একটি কমেডি এক্টর সেই ভূমিকার জন্য খুব উপযুক্ত না হলেও।
রুবি এই হেল্পার মেথডগুলো প্রায় সকল বেসিক অবজেক্টে রুবি স্ট্যান্ডার্ড লাইব্রেরিতে অফার করে।
:foo.to_s # => "foo"
10.0.to_i # => 10
"10".to_i # => 10
এই মেথডগুলো, বিশেষ করে #to_s, রুবির অধিকাংশ বেসিক টাইপে ইমপ্লিমেন্টেড থাকে। কাস্টিং প্রায় সবসময় একটি মান রিটার্ন করে, কিন্তু ফলাফল সেটা নাও হতে পারে যা আপনি আশা করেন।
"foo10".to_i # => 0
[1, 2, 3].to_s # => "[1, 2, 3]"
{ :foo => :bar }.to_s # => "{:foo=>:bar}"
{ :foo => :bar }.to_a # => [[:foo, :bar]]
Object.to_s # => "Object"
Object.new.to_s # => "#"
#to_s, #to_i, #to_a এবং #to_h হেল্পারগুলো কল করা যেকোনো মানকে নির্বাচিত টাইপে ফোর্স করে। সেগুলো কোয়ার্সন হওয়া টাইপের একটি রিপ্রেজেন্টেশন রিটার্ন করে মানফলাফলের উপর নির্ভর না করে।
ইম্প্লিসিট কোয়ার্সন মেথড
যে মানগুলো আমরা কাস্ট করতে চাই সেটার টাইপের মত আচরণ না করলে টাইপ কাস্টিং মেথড কল করলে এরর বা ডেটা লস হতে পারে। রুবি ইম্প্লিসিট কোয়ার্সন মেথডও অফার করে যা শুধু তখন মান রিটার্ন করে যখন অবজেক্টগুলো টাইপের মত আচরণ করে। এইভাবে আমরা নিশ্চিত হতে পারি যে মানটি আমাদের চাওয়া টাইপের মত আচরণ করছে। এই ইম্প্লিসিট কোয়ার্সন মেথডগুলো হচ্ছে #to_str, #to_int, #to_ary এবং #to_hash।
ইম্প্লিসিট কোয়ার্সন লিওনার্ড নিময়কে স্পক ছাড়া যেকোনো ভূমিকায় কাস্ট করার মতো। তারা কাজ করবে যদি চরিত্রটি স্পকের suficiente কাছাকাছি হয়, কিন্তু ব্যর্থ হবে যদি না হয়। #to_str হেল্পার চেষ্টা করে স্ট্রিংএ কনভার্ট করতে, কিন্তু NoMethodError রেইজ করবে যদি অবজেক্টটি মেথডটি ইমপ্লিমেন্ট না করে এবং ইম্প্লিসিটলি কোয়ার্সন করা না যায়।
10.to_int # => 10
10.0.to_int # => 10
require "bigdecimal"
BigDecimal.new("10.0000123").to_int # => 10
# ব্যর্থ কোয়ার্সন
"10".to_int # => NoMethodError
"foo10".to_int # => NoMethodError
[1, 2, 3].to_str # => NoMethodError
{ :foo => :bar }.to_str # => NoMethodError
{ :foo => :bar }.to_ary # => NoMethodError
Object.to_str # => NoMethodError
Object.new.to_str # => NoMethodError
দেখতে পাচ্ছি, রুবি এখন কিছুটা কঠোর যে সেটি কী কোয়ার্সন করবে আর কী নয়। যদি কোয়ার্সন সম্ভব না হয়, #to_* মেথডটি অবজেক্টে ইমপ্লিমেন্টেড না থাকলে এটিকে কল করলে NoMethodError রেইজ হবে।
ইম্প্লিসিট কোয়ার্সন ব্যবহার করলে, যেমন #to_str, আমরা ফাংশনকে একটি স্ট্রিং অবজেক্ট রিটার্ন করতে বলি, শুধু যদি মূল টাইপটিও স্ট্রিংএর মত আচরণ করে। এই কারণে, #to_str রুবি স্ট্যান্ডার্ড লাইব্রেরিতে শুধু স্ট্রিং-এ ইমপ্লিমেন্টেড।
রুবি কীভাবে ইম্প্লিসিট কোয়ার্সন ব্যবহার করে
কোয়ার্সনের সময় আমরা কী চাইছি সেটা আরও প্রিসাইজ হোনো ছাড়া, ইম্প্লিসিট কোয়ার্সন আর কী কাজে আসে? оказывается রুবি নিজে ইম্প্লিসিট কোয়ার্সন অনেক সিনেরিওতে ব্যবহার করে। উদাহরণস্বরূপ, + দিয়ে অবজেক্ট কম্বাইন করার সময়।
name = "world!"
"Hello " + name # => "Hello world!"
# #to_str ছাড়া
class Name
def initialize(name)
@name = name
end
end
"Hello " + Name.new("world!") # => TypeError: no implicit conversion of Name into String
এখানে, রুবি একটি TypeError রেইজ করে কারণ এটি Name টাইপকে String-এ ইম্প্লিসিটলি কনভার্ট করতে পারছে না।
যদি আমরা ক্লাসে #to_str ইমপ্লিমেন্ট করি, রুবি জানবে Name টাইপ কীভাবে কোয়ার্সন করতে হয়।
# #to_str সহ
class Name
def to_str
@name
end
end
"Hello " + Name.new("world!") # => "Hello world!"
একই কাজ অ্যারে এবং #to_ary-এর জন্য কাজ করে।
class Options
def initialize
@internal = []
end
def <<(value)
@internal << value
end
end
options = Options.new
options << :foo
[:some_prefix] + options # => TypeError: no implicit conversion of Options into Array
class Options
def to_ary
@internal
end
end
[:some_prefix] + options # => [:some_prefix, :foo]
কিন্তু #to_ary আরও সিনেরিওতে ব্যবহৃত হয়। আমরা এটি ব্যবহার করতে পারি একটি অ্যারেকে আলাদা ভেরিয়েবলে ডিস্ট্রাকচার করার জন্য।
options = Options.new
options << :first
options << :second
options << :third
first, second, third = options
first # => :first
second # => :second
third # => :third
এটি অবজেক্টকে ব্লক প্যারামিটারে কনভার্ট করতেও কাজ করে।
[options].each do |(first, second)|
first # => :first
second # => :second
end
অরো সিনেরিও আছে যেখানে ইম্প্লিসিট কোয়ার্সন মেথডগুলো ব্যবহৃত হয়, যেমন ** সহ #to_hash। এটি ভ্যালুকে #to_hash দিয়ে হ্যাশে কোয়ার্সন করে parse_options মেথডে পাস করার আগে।
class Options
def to_hash
# Options অ্যারে থেকে হ্যাশ তৈরি
Hash[*@internal]
end
end
def parse_options(opts)
opts
end
options = Options.new
options << :key
options << :value
parse_options(**options) # => {:key=>:value}
টাইপ এনফোর্স করা
রুবি আরও রেজিলিয়েন্ট কোয়ার্সন মেথডও অফার করে যখন টাইপ অজানা থাকে এবং আমরা নিশ্চিত হতে চাই সঠিক টাইপ পাওয়ায়। প্রতিটি বেসিক টাইপের জন্য একটি আছে (String(...), Integer(...), Float(...), Array(...), Hash(...), ইত্যাদি)।
String(self) # => "main"
String(self.class) # => "Object"
String(123456) # => "123456"
String(nil) # => ""
Integer(123.999) # => 123
Integer("0x1b") # => 27
Integer(Time.new) # => 1204973019
Integer(nil) # => TypeError: can't convert nil into Integer
String(...) মেথড প্রথমে ভ্যালুতে #to_str কল করার চেষ্টা করে, এবং যখন সেটা ব্যর্থ হয়, তখন এর #to_s মেথড কল করে। সব অবজেক্ট #to_str মেথড ডিফাইন করে না, তাই ইম্প্লিসিট কোয়ার্সন (#to_str) এবং এক্সপ্লিসিট (#to_s) উভয় কাস্টিং মেথড চেক করলে স্ট্রিং কনভারশন সফল হয়ার সম্ভাবনা বাড়ে এবং আপনি চাওয়া মান পাবেন। প্রথমে ইম্প্লিসিট কোয়ার্সন কল করলে আমরা বেশি সম্ভাবনা পাই এমন ফলাফল যা একই মান কিন্তু কোয়ার্সন করা টাইপের, এবং "# এর মত কিছু না হয়।
class MyString
def initialize(value)
@value = value
end
def to_str
@value
end
end
s = MyString.new("hello world")
s.to_s # => "#"
s.to_str # => "hello world"
String(s) # => "hello world"
আপনার যদি ইম্প্লিসিট কাস্টিং মেথড ইমপ্লিমেন্ট করেন, তবে শুধু সেই অবজেক্টের জন্য যেগুলো কোয়ার্সন হওয়া টাইপের মত আচরণ করে, যেমন #to_str আপনার নিজস্ব স্ট্রিং ক্লাসের জন্য।
প্রথমে ইম্প্লিসিট কোয়ার্সন চেষ্টা করার পাশাপাশি, String(...) হেল্পার রিটার্নড টাইপও চেক করে। #to_str শুধু একটি মেথড যা যেকোনো টাইপের মান রিটার্ন করতে পারে, এমনকি নন-স্ট্রিংও। রিকোয়েস্ট করা টাইপের মান পাওয়া নিশ্চিত করতে String(...) টাইপ মッチ না করলে TypeError রেইজ করে।
class MyString
def to_str
nil
end
end
s = MyString.new("hello world")
s.to_s # => "#"
s.to_str # => nil
String(s) # => "#"
এখানে, আমরা দেখতে পাচ্ছি রুবি #to_str-এর ফলাফল উপেক্ষা করে কারণ সেটি nil রিটার্ন করল, যা স্ট্রিং-টাইপ নয়। বদলে, এটি #to_s-এর ফলাফলে ফলব্যাক করে।
যদি #to_s-ও nil রিটার্ন করে এবং তেজে সঠিক টাইপের না হয়, String(...) একটি TypeError রেইজ করবে।
class MyString
def to_str
nil
end
def to_s
nil
end
end
s = MyString.new("hello world")
s.to_s # => nil
s.to_str # => nil
String(s) # => TypeError: can't convert MyString to String (MyString#to_s gives NilClass)
যদিও সেগুলো টাইপ কোয়ার্সন এনফোর্স করার ক্ষেত্রে বেশি নির্ভরযোগ্য, মনে রাখবেন কাস্টিং হেল্পার মেথডগুলো (String(...), Integer(...), ইত্যাদি) সাধারণত কিছুটা ধীর কারণ সেগুলো দেওয়া ভ্যালুতে বেশি চেক করতে হয়।
উপসংহার
যখন আপনি নিশ্চিত হতে চান একটি অবজেক্টের জন্য সঠিক টাইপের ডেটা ডাল করছেন, টাইপ কোয়ার্সন একটি উপযোগী প্রক্রিয়া। এই পোস্টে, আমরা এক্সপ্লিসিট কাস্টিং হেল্পার এর মত #to_s, #to_i, #to_a এবং #to_h সম্পর্কে আমাদের জ্ঞান তাজা করলাম। আমরা بتلك الأمثلةও দেখেছি যখন ইম্প্লিসিট হেল্পার যেন #to_str, #to_int, #to_ary এবং #to_hash উপকারী এবং কীভাবে রুবি নিজে সেগুলো ব্যবহার করে।
আমরা আশা করি এই টাইপ কোয়ার্সন ওভারভিউ আপনার জন্য উপকারী হয়েছে এবং এক্টর টাইপকাস্টিং অনুপাতটি আপনার ভালো লেগেছে। সবসময়ের মত, যদি আপনার কোনো টপিক থাকে যা আপনি আমাদের কভার করতে চান, আমাদের জানান। আপনার কোনো প্রশ্ন বা মন্তব্য থাকলে, @AppSignal-এ আমাদের লাইন ড্রপ করতে সংকোচ করবেন না।