Result Type — একটি কন্টেইনার টাইপ যা একটি অপারেশনের ফলাফল উপস্থাপন করে যা সাফল্য (Success) বা ব্যর্থতা (Failure) সহ সম্পন্ন হতে পারে। এক্সেপশনের বিপরীতে, Result ত্রুটিটিকে স্ট্যাক আনওয়াইন্ডিং ছাড়াই একটি সাধারণ মান হিসাবে পাস করে, যা প্রত্যাশিত ত্রুটিগুলির হ্যান্ডলিংকে আরও নিরাপদ এবং কম্পোজেবল করে তোলে। Apple Swift Documentation (2026) অনুসারে, Swift-এ Result<Success, Failure> প্রোগ্রাম এক্সিকিউশন বাধা না দিয়ে map এবং flatMap-এর মাধ্যমে স্বয়ংক্রিয় ত্রুটি হ্যান্ডলিং সহ অপারেশন চেইন করার অনুমতি দেয়।
মূল বিষয়
Result Type একটি জেনেরিক টাইপ যা একটি অপারেশনের ফলাফলকে এনক্যাপসুলেট করে যা সফল বা ব্যর্থ হতে পারে। এক্সেপশনের বিপরীতে, যেখানে একটি ত্রুটি স্বাভাবিক প্রবাহকে বাধা দেয় এবং একটি catch ব্লক খুঁজতে স্ট্যাক আনওয়াইন্ডিং প্রয়োজন, Result ত্রুটিটিকে একটি সাধারণ মান হিসাবে পাস করে — কলার সর্বদা একটি অবজেক্ট পায় এবং এটি কীভাবে হ্যান্ডল করবে তা সিদ্ধান্ত নেয়। এটি বিশেষ করে প্রত্যাশিত ত্রুটিগুলির জন্য উপযোগী: অবৈধ ইনপুট, ব্যবসায়িক নিয়ম, সার্ভার প্রত্যাখ্যান, যেখানে এক্সেপশন খুব ভারী একটি প্রক্রিয়া হবে।
Result-এর ধারণা ফাংশনাল প্রোগ্রামিং থেকে উদ্ভূত, যেখানে অনুরূপ টাইপগুলিকে Either (বা Left/Right) বলা হয়। Swift-এ, Result Swift 5.0-এ একটি স্ট্যান্ডার্ড টাইপ হয়েছে; Kotlin-এ, Result<T> স্ট্যান্ডার্ড লাইব্রেরিতে উপস্থিত হয়েছে; Dart 3.0-এ, বিল্ট-ইন Result<T> চালু করা হয়েছে। প্রতিটি ইমপ্লিমেন্টেশন কন্টেইনারের সাথে কাজ করার জন্য পদ্ধতি প্রদান করে: map (সাফল্যের মান রূপান্তর), flatMap (Result ফাংশন চেইন), mapError (ত্রুটি রূপান্তর), fold (উভয় কেস হ্যান্ডল)। Result Type মোবাইল ডেভেলপমেন্টে ফাংশনাল এরর হ্যান্ডলিংয়ের ভিত্তিপ্রস্তর, প্রত্যাশিত পরিস্থিতির জন্য try-catch-এর বিকল্প।
Result-এর মূল সুবিধা হল কম্পোজেবিলিটি। আপনি একাধিক অপারেশন, যার প্রতিটি ব্যর্থ হতে পারে, নেস্টেড try-catch ব্লক ছাড়াই একটি চেইনে একত্রিত করতে পারেন। যদি চেইনের কোনো অপারেশন Failure-এর সাথে ব্যর্থ হয়, পুরো চেইনটি বাধাগ্রস্ত হয় এবং Failure ফেরত দেয় — একটি if শর্ত বা catch ব্লক ছাড়াই। এটি কোডকে রৈখিক এবং পড়া সহজ করে তোলে, বিশেষ করে একাধিক অনুক্রমিক API অনুরোধ বা ব্যবসায়িক নিয়ম যাচাই সহ পরিস্থিতিতে।
Swift-এ, Result<Success, Failure> দুটি কেস সহ একটি enum: .success(Success) এবং .failure(Failure), যেখানে Failure Error প্রোটোকল দ্বারা সীমাবদ্ধ। Swift Result map, flatMap, mapError এবং get পদ্ধতি সহ একটি সম্পূর্ণ ফাংশনাল টাইপ। get একটি বিশেষ পদ্ধতি: এটি ফলাফল সফল হলে Success মান ফেরত দেয় এবং Failure হলে ত্রুটি থ্রো করে। এটি Result-কে ফাংশনাল এবং এক্সেপশন-ভিত্তিক শৈলীর মধ্যে একটি সেতু হিসেবে কাজ করতে দেয়: চেইনে map/flatMap-এর মাধ্যমে হ্যান্ডল করুন এবং শেষে throws কোডের সাথে সংহত করতে do-catch-সহ get ব্যবহার করুন।
Swift Result জেনেরিক প্যারামিটার সহ একটি enum, যা কম্পাইলারকে switch বা do-catch-এর মাধ্যমে এক্সজস্টিভ হ্যান্ডলিং প্রয়োগ করতে দেয়। আপনি যদি NetworkError enum-এ একটি নতুন কেস যোগ করেন, কম্পাইলার সমস্ত switch এক্সপ্রেশনে ত্রুটি তৈরি করবে যেখানে সেই কেসটি হ্যান্ডল করা হয়নি। এক্সজস্টিভ চেকিং হল এক্সেপশনের উপর Result-এর প্রধান সুবিধা: কম্পাইলার গ্যারান্টি দেয় যে বিল্ড টাইমে সমস্ত সম্ভাব্য ত্রুটির হিসাব নেওয়া হয়েছে। এর বিপরীতে, Swift-এ কম্পাইলার দ্বারা এক্সেপশন চেক করা হয় না (শুধুমাত্র throws ঘোষণা করা হয়, কিন্তু ত্রুটির ধরন নয়)।
enum NetworkError: Error {
case badURL
case requestFailed(String)
case decodingFailed
}
func fetchUser(id: Int) -> Result<User, NetworkError> {
guard let url = URL(string: "https://api.example.com/users/\(id)") else {
return .failure(.badURL)
}
let result = performRequest(url: url)
switch result {
case let .success(data):
if let user = try? JSONDecoder().decode(User.self, from: data) {
return .success(user)
}
return .failure(.decodingFailed)
case let .failure(error):
return .failure(.requestFailed(error.localizedDescription))
}
}
// switch-এর সাথে ব্যবহার
let result = fetchUser(id: 42)
switch result {
case .success(let user):
showUser(user)
case .failure(.badURL):
logError("Invalid URL")
case .failure(.requestFailed(let msg)):
showAlert(msg)
case .failure(.decodingFailed):
logError("Decoding error")
}
Result<Success, Failure> টাইপ স্তরে ত্রুটিকে টাইপ করার অনুমতি দেয়: fetchUser ফাংশন Result<User, NetworkError> ফেরত দেয়, যেখানে NetworkError তিনটি কেস সহ একটি কংক্রিট enum। কলার এক্সজস্টিভ কভারেজ সহ switch-এর মাধ্যমে প্রতিটি কেস হ্যান্ডল করে — কম্পাইলার চেক করে যে সমস্ত ভেরিয়েন্ট হ্যান্ডল করা হয়েছে। এক্সজস্টিভ switch হল এক্সেপশনের উপর Result-এর একটি মূল সুবিধা: কম্পাইলার গ্যারান্টি দেয় যে আপনি .badURL, .requestFailed বা .decodingFailed হ্যান্ডল করতে ভুলবেন না। এক্সেপশনের সাথে, কম্পাইলার হ্যান্ডলিংয়ের প্রয়োজন হয় না, এবং কোড রিভিউতে একটি অনুপস্থিত catch(.badURL) সহজেই এড়িয়ে যেতে পারে।
Kotlin-এ, Result<T> স্ট্যান্ডার্ড লাইব্রেরির একটি বিল্ট-ইন টাইপ যা সাফল্য (T) বা ত্রুটি (Throwable) উপস্থাপন করে। Swift-এর বিপরীতে, Kotlin Result একটি কংক্রিট ত্রুটির ধরন নির্দিষ্ট করার অনুমতি দেয় না — শুধুমাত্র Throwable। এটি সরলতার জন্য করা হয়েছে কিন্তু when এক্সপ্রেশনের মাধ্যমে অতিরিক্ত ত্রুটি টাইপ চেক প্রয়োজন। Kotlin Result fold (উভয় কেস হ্যান্ডল), getOrNull (সাফল্য বা null), getOrDefault (সাফল্য বা ডিফল্ট), map, recover, andThen ফাংশন সমর্থন করে। Kotlin-এর একটি বিশেষত্ব: Result ফাংশন সীমানা জুড়ে সরাসরি প্রচারের জন্য ডিজাইন করা হয়নি — অতিরিক্ত অভিযোজন ছাড়া এটি Android SDK পদ্ধতি বা Kotlin Coroutines API-এর জন্য রিটার্ন টাইপ হিসাবে ব্যবহার করা যায় না।
fun parseJson(input: String): Result<JsonObject> {
return runCatching {
JsonParser.parseString(input).asJsonObject()
}
}
fun validateEmail(email: String): Result<String> {
return if (email.contains("@")) {
Result.success(email.trim())
} else {
Result.failure(IllegalArgumentException("Invalid email"))
}
}
data class SignupData(val name: String, val email: String)
fun processSignup(name: String, email: String): SignupResult {
return validateEmail(email).fold(
onSuccess = { validateName(name) },
onFailure = { SignupResult.Error("Invalid email") }
)
}
runCatching Kotlin-এ একটি সুবিধাজনক র্যাপার যা যেকোনো এক্সেপশন ক্যাচ করে এবং Result.failure ফেরত দেয়। validateEmail যাচাইয়ের উপর নির্ভর করে Result.success বা Result.failure ফেরত দেয়। fold উভয় কেস কম্প্যাক্টলি হ্যান্ডল করে: Success-এ, পরবর্তী যাচাই ফাংশন কল করা হয়; Failure-এ, SignupResult.Error ফেরত দেওয়া হয়। গুরুত্বপূর্ণ: Kotlin Result ডেটা ক্লাস ফিল্ডে সংরক্ষণ বা suspend ফাংশন সীমানা জুড়ে সরাসরি প্রেরণের জন্য ডিজাইন করা হয়নি — Jetpack Compose বা MVVM-এ UI অবস্থা উপস্থাপনের জন্য কাস্টম সিলড ক্লাস (Success/Error/Loading) ব্যবহার করুন।
Dart 3.0 থেকে, স্ট্যান্ডার্ড লাইব্রেরিতে একটি বিল্ট-ইন Result<T> অন্তর্ভুক্ত রয়েছে — দুটি কনস্ট্রাক্টর সহ একটি সিলড ক্লাস: T.ok() (সাফল্য) এবং Error.error() (Object এবং StackTrace সহ ত্রুটি)। Dart 3.0-এর আগে, Flutter ডেভেলপাররা dartz প্যাকেজ থেকে Either<L, R> বা কাস্টম সিলড ক্লাস ব্যবহার করতেন। Dart-এ বিল্ট-ইন Result ন্যূনতম: এটি সরাসরি map/flatMap প্রদান করে না — এই ফাংশনগুলি when-এর মাধ্যমে বা এক্সটেনশন পদ্ধতি ব্যবহার করে ইমপ্লিমেন্ট করতে হবে। গুরুতর ফাংশনাল হ্যান্ডলিংয়ের জন্য, fpdart-এর Either map, flatMap, mapLeft, fold, andThen এবং bind অপারেটর সহ আরও শক্তিশালী সমাধান রয়ে গেছে।
Either<L, R> fpdart প্যাকেজের একটি বাম-পক্ষপাতী টাইপ, যেখানে Left ত্রুটি এবং Right সাফল্য। বিল্ট-ইন Result<T>-এর বিপরীতে, Either টাইপ প্যারামিটার স্তরে (L) ত্রুটিকে টাইপ করে, যা কম্পাইল টাইমে ত্রুটি টাইপ পার্থক্য করতে দেয়। fpdart প্যাকেজ ফাংশনাল কম্বিনেটরের একটি সম্পূর্ণ সেট প্রদান করে: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — নেস্টেড Either), andThen (রূপান্তর ছাড়াই চেইন), fold (Either থেকে প্রস্থান), getOrElse (ডিফল্ট মান)। ফাংশনাল পদ্ধতির Flutter অ্যাপ্লিকেশনের জন্য, Either হল ডি ফ্যাক্টো স্ট্যান্ডার্ড।
import 'dart:convert';
import 'package:fpdart/fpdart.dart';
class UserService {
Either<AppError, User> fetchUser(String id) {
try {
final response = await http.get(
Uri.parse('https://api.example.com/users/$id')
);
if (response.statusCode == 200) {
final user = User.fromJson(
json.decode(response.body)
);
return Either.of(user);
}
return Either.left(
AppError.serverError(response.statusCode)
);
} on SocketException catch (e) {
return Either.left(AppError.networkError(e.message));
}
}
}
// fold-এর সাথে ব্যবহার
final result = await service.fetchUser('42');
result.fold(
(left) => showError(left.message),
(right) => showUser(right),
);
Either<L, R> fpdart থেকে একটি বাম-পক্ষপাতী টাইপ: Left — ত্রুটি, Right — সাফল্য। fetchUser Either<AppError, User> ফেরত দেয়, যেখানে AppError কংক্রিট ত্রুটি টাইপ (serverError, networkError) সহ একটি সিলড ক্লাস। fold উভয় কেস হ্যান্ডল করে: প্রথম কলব্যাক Left (ত্রুটি)-এর জন্য, দ্বিতীয়টি Right (সাফল্য)-এর জন্য। Dart 3.0-এ, বিল্ট-ইন Result-ও fold সমর্থন করে, কিন্তু map/flatMap প্রদান করে না। চেইনের জন্য, fpdart-এর Either map, flatMap (bind), mapLeft, andThen প্রদান করে — ত্রুটি কম্পোজিশনের জন্য ফাংশনাল কম্বিনেটরের একটি সম্পূর্ণ সেট।
এক্সেপশনের উপর Result-এর প্রধান সুবিধা হল কম্পোজিশন। আপনার যদি একাধিক অপারেশন থাকে, যার প্রতিটি ব্যর্থ হতে পারে, আপনি একটি নেস্টেড if বা try-catch ছাড়াই map এবং flatMap ব্যবহার করে সেগুলিকে চেইন করতে পারেন। map সাফল্যের মান রূপান্তর করে: Result.success(x) -> Result.success(f(x)). flatMap (যাকে bind বা andThen-ও বলা হয়) সেই ক্ষেত্রগুলির জন্য যেখানে রূপান্তর নিজেই একটি Result ফেরত দেয়: Result.success(x) -> f(x) -> Result<Y>। যদি কোনো ধাপ Failure-এর সাথে ব্যর্থ হয়, পরবর্তী অপারেশনগুলি এক্সিকিউট হয় না — চেইনটি বাধাগ্রস্ত হয়।
data class UserRequest(val userId: String, val token: String)
sealed class AuthError {
data object InvalidToken : AuthError()
data class UserNotFound(val id: String) : AuthError()
}
typealias Outcome<T> = Either<AuthError, T>
fun validateToken(token: String): Outcome<String> =
if (token.isNotBlank()) Either.right(token)
else Either.left(AuthError.InvalidToken)
fun fetchProfile(userId: String): Outcome<Profile> =
if (userId == "42") Either.right(Profile("Alice"))
else Either.left(AuthError.UserNotFound(userId))
// flatMap-এর মাধ্যমে কম্পোজিশন (fpdart-এ andThen)
val result = validateToken("abc123")
.flatMap { fetchProfile("42") }
.map { it.name }
.getOrElse { "Guest" }
println(result) // "Alice"
চেইন: validateToken -> fetchProfile -> map name -> getOrElse “অতিথি”. যদি validateToken Left (InvalidToken) ফেরত দেয়, চেইনটি বাধাগ্রস্ত হয় এবং “অতিথি” ফেরত দেয়। যদি fetchProfile Left (UserNotFound) ফেরত দেয় —ও “অতিথি”. যদি উভয় অপারেশন সফল হয় — প্রোফাইলের নাম। flatMap Either ফাংশনগুলিকে একত্রিত করার অনুমতি দেয়, যার প্রতিটি ব্যর্থ হতে পারে, একটি রৈখিক চেইনে। ঐতিহ্যগত এক্সেপশন শৈলীতে, একই কোডের জন্য দুটি নেস্টেড try-catch বা null চেক প্রয়োজন হবে। শেষে getOrElse কম্পোজিশন থেকে প্রস্থান বিন্দু, যা Failure ক্ষেত্রের জন্য একটি ডিফল্ট মান প্রদান করে।
Result এবং এক্সেপশন পারস্পরিক এক্সক্লুসিভ পদ্ধতি নয়। প্রতিটির নিজস্ব ডোমেন রয়েছে এবং একটি ভালভাবে ডিজাইন করা মোবাইল অ্যাপ্লিকেশনে উভয়ই ব্যবহৃত হয়। পছন্দ নির্ভর করে ত্রুটিটি প্রত্যাশিত নাকি অপ্রত্যাশিত কিনা তার উপর। Result প্রত্যাশিত ত্রুটিগুলির জন্য যা ব্যবসায়িক লজিকের অংশ: অবৈধ ইমেল, অপর্যাপ্ত তহবিল, রেট সীমা অতিক্রম করেছে। এক্সেপশন অপ্রত্যাশিত সিস্টেম ত্রুটিগুলির জন্য: নেটওয়ার্ক ক্ষতি, OutOfMemoryError, NullPointerException (যা হওয়া উচিত নয় কিন্তু ঘটে)।
| মাপকাঠি | Result Type | এক্সেপশন (Exception/Error) |
|---|---|---|
| ত্রুটির ধরন | প্রত্যাশিত (ব্যবসায়িক লজিক) | অপ্রত্যাশিত (সিস্টেম) |
| কর্মক্ষমতা | কম খরচ (স্ট্যাক আনওয়াইন্ডিং ছাড়া) | উচ্চ খরচ (স্ট্যাক আনওয়াইন্ডিং, StackCapture ক্যাপচার) |
| কম্পোজিশন | map/flatMap-এর মাধ্যমে — রৈখিক চেইন | নেস্টেড try-catch — পড়া কঠিন |
| কম্পাইলার | এক্সজস্টিভ চেকিং (switch/when) | শুধুমাত্র Java-তে চেক করা এক্সেপশন |
| এক্সিকিউশন প্রবাহ | বাধাগ্রস্ত হয় না — ত্রুটি একটি মান হিসাবে | নিকটতম catch পর্যন্ত বাধাগ্রস্ত হয় |
| পরীক্ষা | সহজ: ফলাফল চেক, isSuccess/isError নিশ্চিত | assertThrows এবং মক অবজেক্ট প্রয়োজন |
| কখন ব্যবহার করবেন | ব্যবসায়িক যাচাই, অনুরোধ চেইন, ফর্ম | নেটওয়ার্ক ক্ষতি, I/O ত্রুটি, সিস্টেম ক্র্যাশ |
ব্যবহারিক নিয়ম: যদি ত্রুটিটি সাধারণ অ্যাপ্লিকেশন ওয়ার্কফ্লোর অংশ হয় (ব্যবহারকারী একটি অবৈধ ইমেল প্রবেশ করিয়েছে, অপর্যাপ্ত অনুমতি) — Result ব্যবহার করুন। যদি ত্রুটিটি একটি ব্যতিক্রমী পরিস্থিতি হয় (সার্ভার সাড়া দিচ্ছে না, মেমরি শেষ) — এক্সেপশন ব্যবহার করুন। মোবাইল ডেভেলপমেন্টে, স্তর সীমানায় Result (UseCase -> ViewModel) এবং স্তরের ভিতরে এক্সেপশন (API -> Repository) একটি সাধারণ প্যাটার্ন যা উভয় পদ্ধতির সুবিধাগুলিকে একত্রিত করে।
বিদ্যমান এক্সেপশন-ভিত্তিক কোডকে Result-এ মাইগ্রেশন ধীরে ধীরে হওয়া উচিত। স্তর সীমানা দিয়ে শুরু করুন: throws ফাংশন কলগুলিকে Result { try ... } (Swift) বা runCatching { ... } (Kotlin)-এ মোড়ান। তারপর Repository এবং UseCase পদ্ধতির রিটার্ন টাইপ Result/Either দিয়ে প্রতিস্থাপন করুন, অভ্যন্তরীণ ইমপ্লিমেন্টেশন এক্সেপশনে রেখে। শেষ ধাপে, ViewModel মাইগ্রেট করুন: এক্সেপশন সহ UiState-এর পরিবর্তে, একটি সিলড ক্লাস UiState<T> (Loading, Success, Error) ব্যবহার করুন, যেখানে Error একটি Throwable নয় বরং একটি ডোমেন ত্রুটি সংরক্ষণ করে। ক্রমিক মাইগ্রেশন বৈশ্বিক রিফ্যাক্টরিং ছাড়াই প্রতিটি স্তর আলাদাভাবে পরীক্ষা করার অনুমতি দেয়।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
Optional (T?) একটি মানের উপস্থিতি বা অনুপস্থিতি উপস্থাপন করে — nil মানে “কোনো ডেটা নেই” কিন্তু কারণ ব্যাখ্যা করে না। Result (Success/Failure) শুধুমাত্র সাফল্যই নয় বরং একটি কংক্রিট টাইপ সহ ত্রুটির কারণও ধারণ করে। Optional ব্যবহার করুন যখন অনুপস্থিতি স্বাভাবিক (উদাহরণস্বরূপ, একটি ঐচ্ছিক প্রোফাইল ফিল্ড), এবং Result ব্যবহার করুন যখন আপনার ত্রুটি তথ্যের প্রয়োজন হয়।
Swift-এ, Result { try throwingFunc() } ব্যবহার করুন — Result কনস্ট্রাক্টর একটি থ্রোয়িং ক্লোজার নেয়। Kotlin-এ, runCatching { throwingFunc() } ব্যবহার করুন, যা Result<T> ফেরত দেয়। Dart-এ, Result<T>.tryCatch(() => throwingFunc()) ব্যবহার করুন। এটি Result চেইনে এক্সেপশন-ভিত্তিক কোডের সহজ সংহতকরণের অনুমতি দেয়।
সাফল্যের মান পরিবর্তন না করে ত্রুটির ধরন রূপান্তর করতে mapError (Swift) বা mapLeft (Dart/Kotlin-এ Either) ব্যবহার করুন। যদি আপনার উভয় কেস হ্যান্ডল করতে এবং একটি একক মান ফেরত দিতে হয়, fold ব্যবহার করুন। চেইন বাধা না দিয়ে লগিংয়ের জন্য, onFailure (Kotlin) বা একটি উপসর্গ পরিদর্শন বিন্দু ব্যবহার করুন।
হ্যাঁ, কিন্তু সতর্কতার সাথে। K2 কম্পাইলার এবং রিফ্লেকশন বৈশিষ্ট্যের কারণে Kotlin Result<T> সরাসরি suspend ফাংশনের রিটার্ন টাইপ হিসাবে সুপারিশ করা হয় না। Coroutines-এ অবস্থা উপস্থাপনের জন্য আপনার নিজস্ব সিলড ক্লাস NetworkResult<T> (Success, Error, Loading) ব্যবহার করুন। ব্যবসায়িক লজিকে প্রত্যাশিত ত্রুটিগুলির জন্য, Arrow-এর Either একটি আরও শক্তিশালী বিকল্প।
fold একটি পদ্ধতি যা দুটি কলব্যাক নেয়: onSuccess (সাফল্যের ক্ষেত্রে) এবং onFailure (ত্রুটির ক্ষেত্রে), এবং যেকোনো টাইপের একটি একক মান ফেরত দেয়। এটি একটি switch এক্সপ্রেশনের সমতুল্য কিন্তু একটি উচ্চ-ক্রম ফাংশন হিসাবে। fold Result চেইন থেকে প্রধান প্রস্থান বিন্দু, যেখানে আপনি Success/Failure-কে UiState, ব্যবহারকারী-মুখী স্ট্রিং বা অন্য Result-এ রূপান্তর করেন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন