البرمجة الوظيفية في Go: المستوى المتقدم
\n\nGo (Golang) ليست لغة وظيفية بحتة، لكن نظام الأنواع القوي فيها، ودعم الإغلاقات والدوال من الدرجة الأولى يسمح بتطبيق النماذج الوظيفية بفعالية. على المستوى المتقدم، تساعد البرمجة الوظيفية في Go على كتابة كود أكثر قابلية للتنبؤ، وأسهل للاختبار، وأكثر أمانًا للتوازي. في هذه المقالة، سنستعرض المفاهيم الأساسية: الدوال النقية، التركيب، الإغلاقات، الكاري، الأنماط المونادية، والتعامل مع الأخطاء بدون استثناءات.
\n\n1. الدوال النقية والثباتية
\n\nالدالة النقية هي دالة:
\n- \n
- تعيد دائمًا نفس النتيجة لنفس الوسائط (الحتمية). \n
- ليس لها آثار جانبية (لا تغير الحالة الخارجية، ولا تكتب في ملفات/شبكة). \n
في Go، تتحقق الثباتية من خلال تمرير نسخ من البيانات واستخدام الثوابت. مثال على دالة نقية:
\n\npackage main\n\nimport "fmt"\n\n// دالة نقية: لا تغير الشريحة الأصلية، تعيد شريحة جديدة\nfunc DoubleSlice(s []int) []int {\n result := make([]int, len(s))\n for i, v := range s {\n result[i] = v * 2\n }\n return result\n}\n\nfunc main() {\n original := []int{1, 2, 3}\n doubled := DoubleSlice(original)\n fmt.Println(original) // [1 2 3] — بدون تغيير\n fmt.Println(doubled) // [2 4 6]\n}\n\nاستخدام الدوال النقية يبسط إعادة الهيكلة والاختبار، حيث لا حاجة لمراعاة الحالة المخفية.
\n\n2. الدوال عالية الترتيب والإغلاقات
\n\nفي Go، الدوال هي كائنات من الدرجة الأولى: يمكن تمريرها كوسائط، وإعادتها من دوال أخرى، وتخزينها في متغيرات. الإغلاق هو دالة تلتقط المتغيرات من النطاق الخارجي.
\n\nمثال على دالة عالية الترتيب — Filter:
package main\n\nimport "fmt"\n\n// Filter تعيد شريحة جديدة تحتوي على العناصر التي تحقق المسند\nfunc Filter[T any](s []T, predicate func(T) bool) []T {\n var result []T\n for _, v := range s {\n if predicate(v) {\n result = append(result, v)\n }\n }\n return result\n}\n\nfunc main() {\n numbers := []int{1, 2, 3, 4, 5, 6}\n even := Filter(numbers, func(n int) bool { return n%2 == 0 })\n fmt.Println(even) // [2 4 6]\n}\n\nتُستخدم الإغلاقات غالبًا لإنشاء مصانع دوال أو لتغليف الحالة:
\n\npackage main\n\nimport "fmt"\n\n// Counter تنشئ إغلاقًا يخزن حالة داخلية\nfunc Counter() func() int {\n count := 0\n return func() int {\n count++\n return count\n }\n}\n\nfunc main() {\n c1 := Counter()\n fmt.Println(c1()) // 1\n fmt.Println(c1()) // 2\n\n c2 := Counter()\n fmt.Println(c2()) // 1 — حالة مستقلة\n}\n\n3. تركيب الدوال والتطبيق الجزئي (الكاري)
\n\nالتركيب هو دمج عدة دوال في دالة واحدة جديدة. في Go، يتحقق التركيب من خلال دوال وسيطة. التطبيق الجزئي (الكاري) يسمح بتثبيت جزء من الوسائط وإعادة دالة تنتظر الباقي.
\n\nمثال على التركيب:
\n\npackage main\n\nimport "fmt"\n\n// Compose تدمج دالتين: f(g(x))\nfunc Compose[A, B, C any](f func(B) C, g func(A) B) func(A) C {\n return func(x A) C {\n return f(g(x))\n }\n}\n\nfunc addOne(x int) int { return x + 1 }\nfunc double(x int) int { return x * 2 }\n\nfunc main() {\n addThenDouble := Compose(double, addOne)\n fmt.Println(addThenDouble(3)) // (3+1)*2 = 8\n}\n\nالكاري على مثال دالة الجمع:
\n\npackage main\n\nimport "fmt"\n\n// Add — نسخة كاري من الجمع\nfunc Add(a int) func(int) int {\n return func(b int) int {\n return a + b\n }\n}\n\nfunc main() {\n addFive := Add(5)\n fmt.