Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات
استخدم Either لتمثيل النجاح والفشل دون طرح الاستثناءات
Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
مشكلة استخدام الاستثناءات في منطق الأعمال
صُممت الاستثناءات للتعامل مع الإخفاقات غير المتوقعة (مؤشر null أو خطأ في الإدخال والإخراج). أما استخدامها للإشارة إلى إخفاقات متوقعة في منطق الأعمال (فشل التحقق أو «لم يُعثر عليه») فيجعل تدفق التحكم غير واضح، ويفرض استخدام try/catch في كل موضع استدعاء، ويخفي الأخطاء في تواقيع الدوال.
ما هو Either<L, R>؟
Either هو نوع تجميعي ذو حالتين: تحتوي Left(value: L) اصطلاحيًا على الخطأ، بينما تحتوي Right(value: R) اصطلاحيًا على قيمة النجاح. ويجعل إرجاع الدالة لـ Either احتمال الفشل واضحًا في توقيعها.
Either في Arrow
أضف Arrow إلى مشروعك لتحصل على Either ومنظومته المتكاملة من العوامل:
// build.gradle.kts
implementation("io.arrow-kt:arrow-core:1.2.4")
// Usage
import arrow.core.Either
import arrow.core.left
import arrow.core.rightإرجاع Either من دالة
أعد value.right() عند النجاح وerror.left() عند الفشل. ويجب على المستدعي معالجة كلا الفرعين:
sealed class UserError { object NotFound : UserError(); data class InvalidEmail(val msg: String) : UserError() }
fun findUser(id: Long): Either<UserError, User> =
if (id <= 0) UserError.NotFound.left()
else User(id, "Alice").right()استهلاك Either باستخدام fold
استخدم fold(ifLeft, ifRight) لمعالجة الحالتين في تعبير واحد:
val result = findUser(1L)
val message = result.fold(
ifLeft = { error -> "Error: $error" },
ifRight = { user -> "Found: ${user.name}" }
)
println(message)تحويل قيمة Right باستخدام map
تحوّل map { } قيمة Right من دون المساس بـ Left. وهذا يجعل Either من نوع functor، إذ يمكنك تسلسل التحويلات بأمان:
val nameResult: Either<UserError, String> = findUser(1L).map { it.name }التسلسل باستخدام flatMap
تُسلسل flatMap { } العمليات الحسابية التي تُعيد بدورها Either. وإذا أعادت أي خطوة Left، يتوقف التسلسل فورًا وينتشر الخطأ من دون تنفيذ الخطوات اللاحقة:
fun validateEmail(email: String): Either<UserError, String> =
if (email.contains("@")) email.right()
else UserError.InvalidEmail("bad format").left()
fun createUser(email: String): Either<UserError, User> =
validateEmail(email).flatMap { validEmail ->
User(1L, validEmail).right()
}Either.catch لتغليف الاستثناءات
تنفّذ Either.catch { } كتلةً برمجية وتغلف أي استثناء على هيئة Left. استخدمها عند الحد الفاصل بين الشيفرة القديمة التي ترمي استثناءات والنطاق المعتمد على Either:
val result: Either<Throwable, User> = Either.catch {
userRepository.findOrThrow(id)
}التعافي باستخدام getOrElse وorElse
تستخرج getOrElse { default } قيمة Right أو تُعيد قيمة افتراضية. أما orElse { alternativeEither } فتستبدل Left بعملية أخرى:
val user: User = findUser(0L).getOrElse { User(-1L, "Guest") }
val user2: Either<UserError, User> = findUser(0L).orElse { User(99L, "Default").right() }مطابقة الأنماط باستخدام when
استخدم when مع فئة sealed شبيهة بـ Either أو مع Either نفسه باستخدام is Either.Left / Either.Right لمعالجة الحالات معالجةً شاملة:
when (val r = findUser(1L)) {
is Either.Left -> println("Error: ${r.value}")
is Either.Right -> println("User: ${r.value.name}")
}Either في طبقات الخدمات
أعد Either من دوال المستودع والخدمة. وحوّله إلى استجابات HTTP على مستوى مسار Ktor، مع إبقاء منطق الأعمال مستقلًا عن شؤون إطار العمل:
get("/users/{id}") {
val id = call.parameters["id"]?.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest)
userService.findById(id).fold(
ifLeft = { call.respond(HttpStatusCode.NotFound) },
ifRight = { call.respond(it) }
)
}تحقق سريع
ماذا تفعل flatMap { } مع قيمة Either.Left؟
مراجعة: Either<L, R>
النقاط الأساسية:
- يجعل
Eitherمسار الفشل واضحًا في توقيع الدالة Right= نجاح؛Left= فشل، وفقًا لل convention المتبعmap { }— تحويل قيمة النجاح؛flatMap { }— تسلسل العمليات التي قد تفشلfold(ifLeft, ifRight)— استهلاك الحالتين في تعبير واحدEither.catch { }— تغليف الشيفرة التي ترمي استثناءات عند الحدود
الأسئلة الشائعة
هل درس «Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات» مجاني؟
نعم — نص درس «Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.
ماذا ستتعلم في «Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات»؟
استخدم Either لتمثيل النجاح والفشل دون طرح الاستثناءات تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟
لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «Either<L, R>: معالجة الأخطاء ذات الأنواع من دون استثناءات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟
نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- Either : معالجة الأخطاء ذات الأنواع من دون استثناءات
- Arrow Raise DSL: تركيب الأخطاء المعرّفة بالأنواع
- Option وNullable: متى تستخدم كلًّا منهما
- نمذجة المجالات الوظيفية باستخدام الأنواع الأساسية في Arrow