البرمجة الوظيفية في Swift: المستوى المتقدم
تم تصميم Swift في الأصل كلغة متعددة النماذج، مما يجعله منصة مثالية لتطبيق تقنيات البرمجة الوظيفية المتقدمة. في هذه المقالة، سنستعرض مفاهيم تتجاوز الإغلاقات الأساسية و map/filter/reduce — سنتحدث عن functors و monads و composition functions و currying و memoization. ستساعدك هذه الأدوات على كتابة كود أكثر قابلية للتنبؤ والاختبار والتعبير لتطبيقات iOS و macOS.
Functors و Monads: العمل مع السياقات
Functor هو نوع يقوم بتنفيذ دالة map، مما يسمح بتطبيق تحويل على القيمة داخل السياق. في Swift، الأمثلة الكلاسيكية للـ functors هي Optional و Array.
// Functor Optionallet value: Int? = 42let result = value.map { $0 * 2 } // Optional(84)
// Functor Arraylet numbers = [1, 2, 3]let doubled = numbers.map { $0 * 2 } // [2, 4, 6]Monad هو functor مع عملية إضافية flatMap (أو >>= في Haskell). تسمح هذه العملية بـ "فك" السياقات المتداخلة. يدعم Swift الـ monads ضمنيًا من خلال بروتوكول Monad، لكن الأنواع الرئيسية (Optional و Result و Async) تنفذ هذا المنطق.
// Monad Optional: سلسلة عمليات مع احتمال nilfunc parseNumber(_ string: String) -> Int? { return Int(string) }func half(_ number: Int) -> Int? { return number % 2 == 0 ? number / 2 : nil }
let input = "42"let result = input.flatMap(parseNumber).flatMap(half) // Optional(21)
// Monad Result لمعالجة الأخطاءenum AppError: Error { case invalidInput }func safeDivide(_ a: Int, _ b: Int) -> Result<Int, AppError> { guard b != 0 else { return .failure(.invalidInput) } return .success(a / b)}
let divisionResult = safeDivide(10, 2).flatMap { safeDivide($0, 1) } // .success(5)تطبيق متقدم: إنشاء نوع monad مخصص للعمل مع العمليات غير المتزامنة.
struct Async<T> { let run: (@escaping (T) -> Void) -> Void func map<U>(transform: @escaping (T) -> U) -> Async<U> { return Async<U> { callback in self.run { value in callback(transform(value)) } } } func flatMap<U>(transform: @escaping (T) -> Async<U>) -> Async<U> { return Async<U> { callback in self.run { value in transform(value).run(callback) } } }}Composition Functions: بناء pipeline
Composition functions هي إنشاء دالة جديدة من خلال تطبيق عدة دوال بشكل متسلسل. في Swift، يمكن تحقيق ذلك عبر عامل تشغيل أو إغلاقات.
// عامل تشغيل التركيبprecedencegroup CompositionPrecedence { associativity: left higherThan: FunctionArrowPrecedence}
infix operator >>> : CompositionPrecedence
func >>>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C { return { x in g(f(x)) }}
// مثال: التحقق من الصحة والتنسيقlet trimWhitespace: (String) -> String = { $0.trimmingCharacters(in: .whitespaces) }let capitalize: (String) -> String = { $0.capitalized }let addExclamation: (String) -> String = { $0 + "!" }
let processString = trimWhitespace >>> capitalize >>> addExclamationprint(processString(" hello world ")) // "Hello World!"التركيب مفيد بشكل خاص عند العمل مع سلاسل تحويل البيانات، مثل معالجة استجابات الشبكة.
// تركيب متقدم مع معالجة الأخطاءtypealias ResultFunction = (T) -> Result<U, Error>
func compose(_ f: @escaping ResultFunction<T, U>, _ g: @escaping ResultFunction<U, V>) -> ResultFunction<T, V> { return { input