البرمجة الوظيفية في Kotlin: المستوى المتقدم
\n\nتم تصميم Kotlin في الأصل كلغة متعددة النماذج، تجمع بسلاسة بين المنهجين الكائني والوظيفي. بفضل إمكانيات مثل الدوال عالية المستوى، تعبيرات لامدا، ثبات البيانات (val)، ونظام الأنواع القوي، تتيح Kotlin تنفيذ مفاهيم معقدة للبرمجة الوظيفية (FP). في هذه المقالة، سنستعرض التقنيات المتقدمة للبرمجة الوظيفية في Kotlin التي ستساعدك على كتابة كود أنظف وأكثر قابلية للتنبؤ والاختبار.
\n\n1. المؤثرات (Functors) والمؤثرات التطبيقية (Applicative Functors)
\n\nالمؤثر (Functor) هو بنية بيانات تدعم عملية map، والتي تطبق دالة على محتويات البنية مع الحفاظ على شكلها. في Kotlin، المؤثرات القياسية هي المجموعات والأنواع المشابهة لـ Option (مثل T? أو Result).
تنفيذ المؤثر لـ Result
\nلننظر إلى مثال مع النوع Result، الذي يغلف قيمة ناجحة أو خطأ. يمكننا إنشاء مؤثر من خلال تنفيذ طريقة map:
sealed class Result<out T> {\n data class Success<T>(val value: T) : Result<T>()\n data class Error(val exception: Throwable) : Result<Nothing>()\n}\n\nfun <T, R> Result<T>.map(f: (T) -> R): Result<R> = when (this) {\n is Result.Success -> Result.Success(f(value))\n is Result.Error -> this // نحتفظ بالخطأ\n}\n\n// الاستخدام\nval result: Result<Int> = Result.Success(5)\nval mapped = result.map { it * 2 } // Result.Success(10)\n\n\nالمؤثر التطبيقي (Applicative Functor) يوسع إمكانيات المؤثر العادي: فهو يسمح بتطبيق دالة موجودة داخل سياق (مثل Result) على قيمة موجودة في سياق. لهذا، يتم استخدام طريقة ap.
fun <T, R> Result<(T) -> R>.ap(value: Result<T>): Result<R> = when (this) {\n is Result.Success -> value.map(f)\n is Result.Error -> this\n}\n\n// مثال: دمج نتيجتين\nval add: (Int) -> (Int) -> Int = { a -> { b -> a + b } }\nval resultF: Result<(Int) -> (Int) -> Int> = Result.Success(add)\nval resultA: Result<Int> = Result.Success(3)\nval resultB: Result<Int> = Result.Success(7)\nval combined = resultF.ap(resultA).ap(resultB) // Result.Success(10)\n\n\n2. المونادات: Option، Either والعمل مع الآثار الجانبية
\n\nالموناد (Monad) هو نوع ينفذ عمليتين: unit (وضع قيمة في سياق) و flatMap (ربط العمليات المونادية). في Kotlin، يمكن اعتبار T? (النوع القابل للعدم) مونادًا مدمجًا، حيث unit هي القيمة نفسها، و flatMap هو العامل ?.let أو fold.
تنفيذ موناد Option
\n\nsealed class Option<out T> {\n data class Some<T>(val value: T) : Option<T>()\n object None : Option<Nothing>()\n}\n\nfun <T, R> Option<T>.flatMap(f: (T) -> Option<R>): Option<R> = when (this) {\n is Option.Some -> f(value)\n is Option.None -> Option.None\n}\n\nfun <T> T.toOption(): Option<T> = Option.Some(this)\n\n// مثال لسلسلة عمليات\ndata class User(val id: Int, val name: String)\n\nfun findUser(id: Int): Option<User> = if (id == 1) Option.Some(User(1, "Alice")) else Option.None\nfun getEmail(user: User): Option<String> = Option.Some("${user.name}@example.com")\n\nval email = findUser(1).flatMap { getEmail(it) } // Option.Some("Alice@example.com")\n\n\nموناد Either لمعالجة الأخطاء
\nعلى عكس Option، يخزن Either معلومات عن الخطأ:
sealed class Either<out L, out R> {\n data class Left<L>(val value: L) : Either<L, Nothing>()\n