Either: ماهيته وطريقة عمله والتطبيق العملي

المؤلف: IT Sectr نُشر: 2026-05-26 وقت القراءة: 8 دق

Either هو نوع وظيفي يمثل قيمة من أحد احتمالين: Left للخطأ و Right للنجاح. على عكس الاستثناءات، يجعل Either معالجة الأخطاء صريحة على مستوى الأنواع ولا يتطلب كتل try-catch. وفقاً Arrow، 2026، يُستخدم Either على نطاق واسع في مشاريع Kotlin لتكوين العمليات التي قد تفشل، دون آثار جانبية.

النقاط الرئيسية

  • Either هو نوع بيانات جبري لتمثيل احتمالين متنافيين: Left (خطأ) و Right (نجاح).
  • Left يحتوي تقليدياً على معلومات الخطأ، Right يحتوي على القيمة الصحيحة.
  • الكتابة الصريحة — يجبر Either على معالجة كلا الاحتمالين في وقت الترجمة، مما يستبعد الأعطال غير المتوقعة.
  • التركيب — يدعم Either دوال map و flatMap و fold لسلاسل التحويل بدون فحوص متداخلة.
  • الفرق عن Result — يمكن لـ Either تخزين نوع خطأ مخصص، وليس فقط Error أو Throwable.

ما هو Either؟

Either هو نوع بيانات جبري، مستعار من البرمجة الوظيفية، يمثل واحداً بالضبط من نوعين محتملين. في سياق معالجة الأخطاء، الاتفاق هو: النوع Left (Left) يحتوي على وصف المشكلة، بينما النوع Right (Right) يحتوي على نتيجة ناجحة.

يعود مفهوم Either إلى لغة Haskell وفئة الأنواع Either a b، حيث a هو نوع القيمة اليسرى و b نوع القيمة اليمنى. في تطوير التطبيقات المحمولة، انتشر Either بفضل مكتبة Arrow للغة Kotlin والمناهج الوظيفية في Swift.

الميزة الرئيسية لـ Either على الاستثناءات هي غياب مسارات التنفيذ المخفية. الدالة التي تُرجع Either تُعلن صراحةً في توقيعها أنها قد تفشل. يتحقق المترجم من معالجة كلا الاحتمالين.

على عكس رمي الاستثناءات، يحافظ Either على شفافية تدفق البيانات. استدعاء دالة تُرجع Either لا يتطلب try-catch من جهة المستدعي — يكفي pattern-matching أو fold. هذا مهم بشكل خاص في البنى ذات النهج التفاعلي، حيث يُرجع كل مصدر بيانات Either وتُبنى سلاسل التحويل عبر map و flatMap.

كيف يعمل Either: بنية Left و Right

يتكون Either من نوعين فرعيين: Left و Right. يمكن أن تكون نسخة Either واحداً منهما فقط في أي لحظة. التوقيع النموذجي في Kotlin هو Either<E, A>، حيث E هو نوع الخطأ و A هو نوع القيمة الناجحة.

النوع Left: حاوية الخطأ

Left يمثل حالة الفشل. على عكس الاستثناءات، لا يقطع Left تدفق التنفيذ — بل يُرجع ببساطة قيمة تحتاج إلى المعالجة. يمكن أن يكون نوع الخطأ أي شيء: String، Int، فئة مختومة مخصصة أو نموذج مجال.

النوع Right: حاوية النجاح

Right يحتوي على النتيجة الصحيحة للعملية. الاسم يعكس الصحة — بالاتفاق، Right يعني النجاح. تُطبق تحويلات map و flatMap على Right، مما يسمح ببناء سلاسل حسابية دون التحقق من كل خطوة.

تركيب Either عبر flatMap

flatMap هي الآلية الرئيسية لتركيب Either. إذا كانت القيمة الحالية هي Right، يُطبق flatMap الدالة المُمرَرة ويُعيد Either جديداً. إذا كانت Left، يتجاوز flatMap التحويل ويُمرر الخطأ. هذا السلوك يُسمى تقييم الدارة القصيرة.

بالإضافة إلى flatMap، يدعم Either mapLeft لتحويل الخطأ، fold لمعالجة كلا الاحتمالين في مكان واحد، و getOrElse لاستخراج قيمة مع قيمة افتراضية. تغطي هذه الدوال جميع السيناريوهات: من الاستخراج البسيط إلى التركيب المعقد مع الاستدعاءات غير المتزامنة في coroutines في Kotlin أو Combine في Swift.

kotlin
fun parseInt(input: String): Either<String, Int> =
    input.toIntOrNull()?.let { Right(it) }
    ?: Left("تعذّر التحويل: $input")

fun divide(a: Int, b: Int): Either<String, Int> =
    if (b == 0) Left("القسمة على صفر")
    else Right(a / b)

val result = parseInt("10")
    .flatMap { divide(it, 2) }

Either في Kotlin: التطبيق العملي

في نظام Kotlin البيئي، تم تنفيذ Either في مكتبة Arrow. تقدم المكتبة القياسية لـ Kotlin Result، لكن Either يوفر مرونة أكبر: نوع خطأ مخصص، تركيب عبر flatMap ودعم للأنماط الوظيفية.

مكتبة Arrow و Either

Arrow هي مكتبة وظيفية للغة Kotlin تضيف Either و Option و Validated وأنواعاً أخرى. Arrow.Either هي فئة مختومة بنوعين فرعيين: ArrowCore.Left و ArrowCore.Right. توفر المكتبة أيضاً امتدادات ملائمة: .getOrElse()، .fold()، .mapLeft().

مثال: Either مع طلب شبكة

لننظر إلى سيناريو واقعي — طلب شبكة في تطبيق Android مع أخطاء محتملة: انقطاع الشبكة، خطأ خادم، استجابة غير صحيحة. يتيح Either دمج جميع الاحتمالات في نوع إرجاع واحد.

kotlin
sealed class NetworkError {
    data class NoConnection(val message: String): NetworkError()
    data class ServerError(val code: Int): NetworkError()
    data class ParseError(val detail: String): NetworkError()
}

suspend fun fetchUser(id: String): Either<NetworkError, User> {
    return try {
        val response = api.getUser(id)
        if (response.isSuccessful) {
            Right(response.body()!!)
        } else {
            Left(NetworkError.ServerError(response.code()))
        }
    } catch (e: IOException) {
        Left(NetworkError.NoConnection(e.message ?: ""))
    }
}

Either في Swift: الفرق عن Result القياسي

في Swift، ابتداءً من الإصدار 5.0، يتوفر النوع المدمج Result المشابه conceptually لـ Either لكن مع قيود: يجب أن يتوافق الخطأ مع بروتوكول Error، وقيمة النجاح يجب أن تكون من نوع واحد. يُنفذ Either في Swift عبر enum مع معاملين عامين.

Result القياسي مقابل Either

Result<Success, Failure> هو النوع المدمج في Swift، حيث Failure: Error. لا يفرض Either قيوداً على نوع الخطأ، مما يسمح بتخزين String أو هياكل مخصصة أو حتى أنواع خطأ متعددة عبر enums متداخلة.

swift
enum Either<E, A> {
    case left(E)
    case right(A)

    func map<B>(_ transform: (A) -> B) -> Either<E, B> {
        switch self {
        case .left(let e): return .left(e)
        case .right(let a): return .right(transform(a))
        }
    }
}

let result: Either<String, Int> = .right(42)
let mapped = result.map { $0 * 2 }

متى نستخدم Either في التطبيقات المحمولة

Either مثالي للسيناريوهات التي تتطلب معالجة أخطاء صريحة وآمنة النوع دون استثناءات. لننظر إلى حالات الاستخدام الرئيسية في تطوير التطبيقات المحمولة بلغتي Kotlin و Swift.

  • طلبات الشبكة — قد يُرجع كل طلب خطأ اتصال أو خادم أو تحليل. يجمع Either جميع الاحتمالات في نوع واحد.
  • التحقق من صحة النماذج — Either مع نوع خطأ مخصص يمثل بشكل ملائم نتيجة التحقق من حقول الإدخال: بريد إلكتروني أو كلمة مرور غير صالحة.
  • مستودع البيانات — يسمح Either بدمج النتائج من مصادر مختلفة (ذاكرة تخزين مؤقت، قاعدة بيانات، شبكة) بنوع خطأ موحد.
  • مفسر الأوامر — إذا كان التطبيق يحلل إدخال المستخدم أو الأوامر، يوفر Either طريقة آمنة النوع للإبلاغ عن الأخطاء.

لا تستخدم Either للعمليات البسيطة دون آثار جانبية — قيمة الإرجاع العادية أكثر موثوقية وأسهل للقراءة. Either أيضاً مبالغ فيه عندما يكون الفشل حالة استثنائية وليس سيناريو متوقعاً.

Either فعال أيضاً عند العمل مع coroutines في Kotlin. يمكن استدعاء دالة تُرجع Either داخل coroutine مع معالجة الأخطاء عبر fold أو mapLeft دون حظر الخيط. هذا مفيد بشكل خاص في تطبيقات Android ذات بنية MVVM، حيث يُرجع كل مستودع Either ويحول ViewModel النتيجة إلى UiState.

الأخطاء الشائعة عند العمل مع Either

المطورون الجدد على Either غالباً ما يرتكبون أخطاء مماثلة. لنستعرض الأكثر شيوعاً وكيفية تجنبها.

  • تجاهل Left — استدعاء .getOrElse() بقيمة افتراضية دون تحليل سبب الخطأ يفقد معنى استخدام Either. تعامل مع الخطأ بشكل صريح.
  • نوع خطأ عام — استخدام String أو Exception كنوع مشترك لجميع Either في المشروع يقلل من أمان النوع. أنشئ فئات مختومة خاصة بالمجال.
  • Either المتداخلة — Either داخل Right (Either<E, Either<E, A>>) يعقد القراءة. استخدم flatMap للتسوية.
  • الخلط مع الاستثناءات — دالة تُرجع Either لكنها ترمي استثناءات داخلياً. هذا يتعارض مع فكرة معالجة الأخطاء الصريحة.

الأسئلة الشائعة

ما الفرق بين Either و Optional؟

Optional يمثل وجود أو غياب قيمة (Some/None)، لكنه لا يخبر عن سبب الغياب. يوفر Either نوعين محددين — أيسر للخطأ وأيمن للنجاح — مما يسمح بنقل سياق الفشل.

هل يمكن استخدام Either في Java؟

في Java لا يوجد Either مدمج، لكن المكتبات Vavr و functionaljava توفر التطبيق. في تطوير Android، Either من Vavr بديل شائع للأسلوب الوظيفي مع lambdas.

متى يكون استخدام sealed class أفضل من Either؟

Sealed class في Kotlin أكثر ملاءمة عندما يكون هناك أكثر من احتمالين أو لها هياكل مختلفة. للنتيجة الثنائية (خطأ/نجاح)، Either أكثر compactness ويوفر أدوات دمج وظيفية جاهزة.

هل يدعم Either تعدد الخيوط؟

Either غير قابل للتغيير وآمن للخيوط افتراضياً. في Kotlin مع coroutines، يتحد Either بشكل ممتاز: يعمل flatMap داخل نطاق coroutine ومعالجة الأخطاء لا تتطلب أقفالاً.

هل يجب استخدام Either لجميع الدوال في المشروع؟

لا. Either مناسب للعمليات ذات الأعطال المتوقعة (شبكة، تحقق، منطق أعمال). للدوال البسيطة والحسابات دون آثار جانبية، النوع العادي أسهل للقراءة ولا يضيف تعقيداً غير ضروري.

الملخص

  • Either هو نوع وظيفي لتمثيل احتمالين: Left (خطأ) و Right (نجاح)، مما يجعل معالجة الأخطاء صريحة على مستوى الأنواع.
  • Left يمكن أن يحتوي على نوع خطأ مخصص — من String إلى فئة مختومة خاصة بالمجال، على عكس Result القياسي.
  • flatMap تمكن من تركيب Either دون فحوص متداخلة: ينتشر الخطأ تلقائياً عبر السلسلة بأكملها.
  • Arrow هي المكتبة الرئيسية لـ Either في Kotlin مع دعم مدمج لـ coroutines والامتدادات.
  • Swift تستخدم Result المدمج، لكن يمكن تنفيذ Either عبر enum مع معاملين عامين للمرونة الكاملة.
  • استخدم Either لـ طلبات الشبكة، التحقق من صحة النماذج والمستودعات — السيناريوهات ذات الأخطاء المتوقعة.
  • تجنب أنواع الخطأ العامة، تجاهل Left و Either المتداخلة — هذا يقلل من فوائد أمان النوع.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا