প্রোগ্রামাররা সাধারণত নিখুঁত সফটওয়্যার লেখার চেষ্টা করেন, আর সাধারণত ব্যর্থ হন।
অপ্রত্যাশিতভাবে জিনিসপত্র ভুল হয়ে যায়, আর সেগুলো সামলানোর ক্ষমতা আমাদের থাকতে হবে।
কিছু ভাষার ডিজাইনার মনে করেন, অগ্রাধিকার হলো যত দ্রুত সম্ভব একটি এরর শনাক্ত করা, তারপর ডিবাগিংয়ে সহায়তার জন্য তথ্যবহুল বার্তা দিয়ে এক্সিকিউশন বন্ধ করে দেওয়া।
ডেটা সায়েন্সের ভাষাগুলো সাধারণত আরও সূক্ষ্ম পদ্ধতি নেয়। কিছু এরর এতটাই গুরুতর যে সাথে সাথে বন্ধ করাই প্রয়োজন, কিন্তু প্রায়শই কোনো সমস্যাকে পরে মোকাবেলা করার বিষয় হিসেবে চিহ্নিত করে এক্সিকিউশন চালিয়ে যাওয়া বেশি ভালো।
Nothingness ধারণায় আমরা দেখেছি যে Julia সমস্যাযুক্ত মানের জন্য বিভিন্ন প্লেসহোল্ডার দেয়: missing, NaN এবং Inf।
কোনো নির্দিষ্ট পরিস্থিতিতে প্রোগ্রাম বন্ধ করার চেয়ে এগুলো ভালো পদ্ধতি কি না, সেটা প্রোগ্রামারের বিচারের ব্যাপার।
নামকরণ প্রসঙ্গে একটি কথা, বিস্তারিত শুরু করার আগে: Julia ডকুমেন্টেশন "error" ও "exception" শব্দ দুটিকে বেশিরভাগ ক্ষেত্রেই বিনিময়যোগ্য হিসেবে দেখে। নিচের বিষয়বস্তুও সমানভাবে অসঙ্গত হতে পারে।
সিলেবাসের এই পর্যায়ে এসে আপনি নিশ্চয়ই Julia-র কাছ থেকে অনেক এরর বার্তা দেখেছেন। উদাহরণস্বরূপ:
julia> Int(3.14)
ERROR: InexactError: Int64(3.14)
একটি ফ্লোটকে ইন্টিজারে কাস্ট করতে গেলে নির্ভুলতা কমে যায়, তাই আমরা একটি InexactError পাই।
InexactError হলো একটি টাইপ, Julia-তে স্ট্যান্ডার্ড হিসেবে বিল্ট-ইন (বর্তমানে ২৫টি) টাইপের একটি।
এগুলো সবই Exception-এর সাবটাইপ:
julia> supertype(InexactError)
Exception
throw()নিজের কোডে কিছু স্ট্যান্ডার্ড এরর টাইপ তৈরি করা কাজে লাগতে পারে।
সব কনক্রিট টাইপের মতো এররগুলোরও কনস্ট্রাক্টর থাকে। এগুলো বিভিন্ন ধরনের আর্গুমেন্ট নেয়, তাই আপনি যেটি ব্যবহার করতে চান তার জন্য ডকুমেন্টেশন দেখুন।
julia> DomainError(42, "out of range")
DomainError(42, "out of range")
এররটি ব্যবহার করতে, কনস্ট্রাক্টরটিকে একটি throw() ফাংশনে মুড়ে দিন:
julia> throw(DomainError(42, "out of range"))
ERROR: DomainError with 42:
out of range
error()দ্রুত ও সহজ সমাধানের জন্য error() ফাংশন সুবিধাজনক হতে পারে।
এটি আর্গুমেন্ট হিসেবে একটি স্ট্রিং (বা স্ট্রিংয়ের উপাদানগুলো) নেয়:
julia> happy = false;
julia> happy || error("😞 something went wrong")
ERROR: 😞 something went wrong
নতুন এরর টাইপ তৈরি করা নীতিগতভাবে খুবই সহজ।
শুধু Exception-এর আরেকটি সাবটাইপ যোগ করুন:
julia> struct MyError <: Exception end
julia> throw(MyError)
ERROR: MyError
অ্যাসার্শনের মূল ধারণা হলো "এই বিবৃতিটি সত্য হওয়া উচিত, তাই এটি মিথ্যা হলে জোরে অভিযোগ করুন।" এর মূল্য মূলত ডিবাগিংয়ের সময়, কারণ প্রোডাকশন কোড কখনোই অ্যাসার্শনে ব্যর্থ হওয়া উচিত নয়।
Types ধারণায় আমরা দেখেছি যে আমরা টাইপ অ্যাসার্শন যোগ করতে পারি, যেমন একটি ফাংশনের রিটার্ন টাইপ পরীক্ষা করার জন্য।
julia> 42::Number
42
julia> "two"::Number
ERROR: TypeError: in typeassert, expected Number, got a value of type String
আরও সাধারণভাবে, @assert ম্যাক্রো আমাদের যেকোনো এক্সপ্রেশন পরীক্ষা করতে দেয় যা একটি বুলিয়ানে মূল্যায়িত হয়:
julia> n = 22;
julia> @assert isodd(n) "n must be odd"
ERROR: AssertionError: n must be odd
try...catch
কিছু এরর অবশ্যম্ভাবীভাবে মারাত্মক, কিন্তু প্রায়শই আমরা আশা করি প্রোগ্রামটি সুন্দরভাবে সামলে উঠবে।
ডিফল্টভাবে, একটি এরর সাথে সাথে বর্তমান ফাংশনটি বন্ধ করে দেয়, এবং এররটি (যেকোনো তথ্যবহুল বার্তাসহ) কল করা ফাংশনের কাছে পাঠানো হয়।
এটি কল স্ট্যাক ধরে উপরে চলতে থাকে, যতক্ষণ না টপ-লেভেল কোড একটি এরর বার্তা দিয়ে বন্ধ হয়ে যায়।
যেকোনো পর্যায়ে, try...catch ব্লক দিয়ে এররটি ধরা যেতে পারে, যা এটি সামলানোর চেষ্টা করে।
julia> n = -1;
julia> try
log_n = log(n)
catch problem
if problem isa DomainError # number out of range
# See next section for more on @warn and @info
@warn "you may have supplied a negative real number: $n"
@info "trying with complex argument"
log_n = log(Complex(n)) # fallback calculation
elseif problem isa MethodError # no idea what n is
@error "please supply a valid argument"
else
rethrow() # the error could be anything else
end
end
┌ Warning: you may have supplied a negative real number: -1
└ @ Main REPL[3]:5
[ Info: trying with complex argument
0.0 + 3.141592653589793im # success
উপরের উদাহরণে, log(n)-এর জন্য n-কে হয় একটি ধনাত্মক বাস্তব মান, নয়তো যেকোনো জটিল মান হতে হবে।
try ... catch ঋণাত্মক বাস্তব মানের সমস্যাগুলো ধরে ফেলে, এবং গাণিতিক স্বরলিপিতে সঠিক জটিল উত্তর iπ ফিরিয়ে দেয়।
যদি আপনি উদাহরণস্বরূপ একটি স্ট্রিং আর্গুমেন্ট দেন, তাহলে ব্যবহারকারীকে তা সংশোধন করতে বলা ছাড়া আর কোনো উপায় নেই।
শেষ ভরসা হিসেবে, আমরা DomainError বা MethodError কোনোটিই নয় এমন যেকোনো কিছুর জন্য rethrow() যোগ করেছি।
দ্রষ্টব্য: কখনো কখনো try...catch-ই আপনার প্রয়োজন, তবে অনুগ্রহ করে এটি অতিরিক্ত ব্যবহার এড়িয়ে চলুন।
যদি পরিবর্তে একটি if...else ব্লক ব্যবহার করা যায়, তাহলে এক্সেপশন ধরা অপেক্ষা এটি অনেক বেশি পারফরম্যান্ট হবে।
খেয়াল রাখুন, উপরে আলোচিত error() ফাংশনটিকে @error ম্যাক্রোর সাথে গুলিয়ে ফেলা উচিত নয়।
ফাংশনটি একটি এক্সেপশন তৈরি করে, যা ধরা না পড়লে কল স্ট্যাক ধরে উপরে পাঠানো হয়।
@error ম্যাক্রো, তার সহযোগী @debug, @info ও @warn-এর সাথে, Logging মডিউলের অংশ, এবং এর উদ্দেশ্য প্রোগ্রামের প্রবাহ না বদলে তথ্যবহুল বার্তা তৈরি করা।
আউটপুট ডিফল্টভাবে টার্মিনালে যায় (গুরুত্ব অনুযায়ী রঙিন), যদিও একটি বাস্তব অ্যাপ্লিকেশনে আরও অনেক সম্ভাবনা থাকে।
julia> @warn "Something looks not quite right"
┌ Warning: Something looks not quite right
└ @ Main REPL[55]:1
julia> @error "Panic!"
┌ Error: Panic!
└ @ Main REPL[56]:1
try...catch-এর অধীনে আগের উদাহরণটিও দেখুন।
এলেনা একটি সংবাদপত্র কারখানার নতুন কোয়ালিটি ম্যানেজার। কোম্পানিতে সদ্য যোগ দেওয়ায় তিনি কারখানার কিছু প্রক্রিয়া পর্যালোচনা করার সিদ্ধান্ত নিয়েছেন, যাতে দেখা যায় কী কী উন্নত করা যায়। তিনি আবিষ্কার করেন যে টেকনিশিয়ানরা অনেক কোয়ালিটি চেক নিজ হাতে করে। অটোমেশনের জন্য এটি একটি ভালো সুযোগ বলে তিনি মনে করেন এবং আপনাকে, একজন ফ্রিল্যান্স ডেভেলপার হিসেবে, কিছু মেশিন মনিটর করার জন্য একটি সফটওয়্যার তৈরি করতে বলেন।
আপনার প্রথম কাজ হলো উৎপাদন কক্ষের আর্দ্রতার মাত্রা মনিটর করার জন্য একটি সফটওয়্যার লেখা। কোম্পানির সফটওয়্যারের সাথে আগে থেকেই একটি সেন্সর যুক্ত আছে, যা নিয়মিতভাবে ঘরের আর্দ্রতার শতাংশ ফেরত দেয়।
সফটওয়্যারে আপনাকে একটি ফাংশন ইমপ্লিমেন্ট করতে হবে, যা আর্দ্রতার শতাংশ বেশি হলে একটি এরর থ্রো করবে।
আর্দ্রতা গ্রহণযোগ্য মাত্রায় থাকলে একটি ইনফো লগ যোগ করা হবে।
ফাংশনটির নাম হবে humiditycheck এবং এটি আর্গুমেন্ট হিসেবে আর্দ্রতার শতাংশ নেবে।
শতাংশ 70%-এর বেশি হলে আপনাকে একটি ErrorException দিয়ে থামতে হবে (সঠিক বার্তাটি গুরুত্বপূর্ণ নয়, তবে তাতে মাপা আর্দ্রতার মাত্রা থাকতে হবে)।
অন্যথায় একটি Info লগ যোগ করুন, যার বার্তা "humidity level check passed: h%", যেখানে h হলো আর্দ্রতার শতাংশ।
julia> humiditycheck(60)
[ Info: humidity level check passed: 60%
julia> humiditycheck(100)
ERROR: humidity check failed: 100%
এলেনা আপনার প্রথম অ্যাসাইনমেন্টে খুব খুশি এবং মেশিনগুলোর তাপমাত্রা মনিটর করার দায়িত্ব আপনাকে দেন। টেকনিশিয়ান গ্রেগের সাথে কথা বলার সময় আপনাকে বলা হয় যে কোনো মেশিনের তাপমাত্রা 500°C ছাড়িয়ে গেলে টেকনিশিয়ানরা অতিরিক্ত গরম হওয়া নিয়ে চিন্তা করতে শুরু করেন।
মেশিনটিতে একটি সেন্সর লাগানো আছে, যা তার অভ্যন্তরীণ তাপমাত্রা মাপে। জেনে রাখুন, সেন্সরটি খুবই সংবেদনশীল এবং প্রায়ই নষ্ট হয়ে যায়। সেক্ষেত্রে টেকনিশিয়ানদের এটি বদলাতে হবে।
আপনার কাজ হলো temperaturecheck নামে একটি ফাংশন ইমপ্লিমেন্ট করা, যা আর্গুমেন্ট হিসেবে তাপমাত্রা নেয় এবং সবকিছু ঠিক থাকলে একটি লগ যোগ করে, অন্যথায় সেন্সর নষ্ট হলে বা মেশিন অতিরিক্ত গরম হতে শুরু করলে একটি এরর থ্রো করে।
পরে এরর অনুযায়ী ভিন্নভাবে প্রতিক্রিয়া জানাতে হবে জেনে, দুই ধরনের এরর আলাদা করার একটি ব্যবস্থা আপনার দরকার।
nothing।
সেক্ষেত্রে আপনাকে একটি ArgumentError দিয়ে থামতে হবে (বার্তাটি গুরুত্বপূর্ণ নয়)।DomainError থ্রো করতে হবে, যাতে মাপা তাপমাত্রা থাকবে।"temperature check passed: t °C", যেখানে t হলো তাপমাত্রা।julia> temperaturecheck(nothing)
ERROR: ArgumentError: sensor is broken
julia> temperaturecheck(800)
ERROR: DomainError with 800:
"overheating detected"
julia> temperaturecheck(500)
[ Info: temperature check passed: 500 °C
পরের কাজের জন্য আপনাকে আরও সাধারণ, সব ধরনের ক্ষেত্রে প্রযোজ্য একটি এরর ডিফাইন করতে হবে।
এটি একটি এরর হওয়া এবং নামটি MachineError হওয়া ছাড়া ইমপ্লিমেন্টেশনের বিস্তারিত গুরুত্বপূর্ণ নয়।
আপনার সুবিধামতো ফিল্ড ও বার্তা যোগ করতে পারেন।
এখন আপনার মেশিন এরর ধরতে পারে এবং আপনার একটি কাস্টম মেশিন এরর আছে, তাই আপনি একটি র্যাপার ফাংশন যোগ করেন, যা সবকিছু কেমন চলছে তা জানাতে পারে। আগের ফাংশনগুলোর লগ রিটার্ন করা ছাড়াও, এই র্যাপারকে ঘটে যাওয়া যেকোনো ধরনের ব্যর্থতার ওপর নির্ভর করে লগ যোগ করতে হবে।
ErrorException থ্রো করলে, "humidity level check failed: h%" বার্তাসহ একটি Error লগ যোগ করতে হবে, যেখানে h হলো আর্দ্রতার শতাংশ।ArgumentError থ্রো করলে, "sensor is broken" বার্তাসহ একটি Warn লগ যোগ করতে হবে।DomainError থ্রো করলে, "overheating detected: t °C" বার্তাসহ একটি Error লগ যোগ করতে হবে, যেখানে t হলো তাপমাত্রা।MachineError থ্রো করতে হবে।humiditycheck ও temperaturecheck-এর লগগুলো যোগ করা হবে।আর্গুমেন্ট হিসেবে আর্দ্রতা ও তাপমাত্রা নেয় এমন একটি machinemonitor() ফাংশন ইমপ্লিমেন্ট করুন।
julia> machinemonitor(42, 450)
[ Info: humidity level check passed: 42%
[ Info: temperature check passed: 450 °C
julia> machinemonitor(42, 550)
[ Info: humidity level check passed: 42%
┌ Error: overheating detected: 550 °C
└ @ Main # output truncated
Error: MachineError
julia> machinemonitor(82, 521)
┌ Error: humidity level check failed: 82%
└ @ Main # output truncated
┌ Error: overheating detected: 521 °C
└ @ Main # output truncated
Error: MachineError
julia> machinemonitor(42, nothing)
[ Info: humidity level check passed: 42%
┌ Warning: sensor is broken
└ @ Main # output truncated
Error: MachineError
Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।