البرمجة الوظيفية في Go: تقنيات متقدمة وأمثلة
لم يتم تصميم Go (Golang) في الأصل كلغة وظيفية بحتة، إلا أن إمكانياتها القوية، مثل الإغلاقات (closures)، والدوال من الدرجة الأولى (first-class functions)، والجوروتينات (goroutines)، تسمح بتطبيق نموذج البرمجة الوظيفية (FP) بنجاح على مستوى متقدم. هذه المقالة مخصصة للمطورين الذين هم على دراية بأساسيات Go ويرغبون في التعمق في التقنيات الوظيفية لكتابة كود أكثر قابلية للتنبؤ والاختبار والتوازي. سنناقش الكارينج (currying)، وتركيب الدوال (function composition)، والعمل مع الهياكل الشبيهة بالموناد (monad-like structures)، ودمج البرمجة الوظيفية مع الجوروتينات.
1. الكارينج والتطبيق الجزئي للدوال
الكارينج هو تحويل دالة تأخذ عدة وسائط إلى سلسلة من الدوال، كل منها تأخذ وسيطًا واحدًا. في Go، حيث لا يوجد دعم مدمج للكارينج، نقوم بتنفيذه من خلال الإغلاقات.
مثال: الكارينج لمسجل قابل للتكوين
package main
import "fmt"
// دالة كارينج: البادئة -> الرسالة -> الإخراجfunc createLogger(prefix string) func(string) { return func(message string) { fmt.Printf("[%s] %s", prefix, message) }}
func main() { infoLogger := createLogger("INFO") errorLogger := createLogger("ERROR")
infoLogger("النظام قيد التشغيل") errorLogger("خطأ في الاتصال") // الإخراج: // [INFO] النظام قيد التشغيل // [ERROR] خطأ في الاتصال}التطبيق الجزئي هو حالة خاصة من الكارينج، حيث نقوم بتثبيت جزء من الوسائط. في Go، هذا مفيد بشكل خاص لإنشاء إصدارات متخصصة من الدوال.
// تطبيق جزئي: تثبيت المعاملfunc multiply(a, b int) int { return a * b}
func partialMultiply(a int) func(int) int { return func(b int) int { return multiply(a, b) }}
func main() { double := partialMultiply(2) triple := partialMultiply(3)
fmt.Println(double(5)) // 10 fmt.Println(triple(5)) // 15}2. تركيب الدوال وخطوط الأنابيب (pipelines)
التركيب هو دمج دوال بسيطة في دوال أكثر تعقيدًا. في Go، يمكننا بناء سلاسل من التحويلات عن طريق تمرير نتيجة دالة كمدخل لدالة أخرى. يعمل هذا بقوة خاصة عند دمجه مع الواجهات (interfaces) والأدوية (generics) (Go 1.18+).
مثال: التركيب لمعالجة السلاسل النصية
package main
import ( "fmt" "strings" "unicode")
type StringTransform func(string) string
// Compose تنشئ تركيبًا لدالتينfunc Compose(f, g StringTransform) StringTransform { return func(s string) string { return f(g(s)) }}
// Pipeline تجمع عدة دوالfunc Pipeline(transforms ...StringTransform) StringTransform { return func(s string) string { for _, t := range transforms { s = t(s) } return s }}
func removeSpaces(s string) string { return strings.ReplaceAll(s, " ", "")}
func toUpper(s string) string { return strings.ToUpper(s)}
func removeDigits(s string) string { var result strings.Builder for _, r := range s { if !unicode.IsDigit(r) { result.WriteRune(r) } } return result.String()}
func main() { // تركيب دالتين cleanAndUpper := Compose(removeSpaces, toUpper) result1 := cleanAndUpper("go 1.18 rocks") fmt.Println(result1) // "GO1.18ROCKS"
// خط أنابيب من ثلاث دوال pipeline := Pipeline(removeSpaces, removeDigits, toUpper) result2 := pipeline("go 1.18 rocks") fmt.Println(result2) // "GOROCKS"}يسمح هذا النهج ببناء تحويلات معقدة من كتل بسيطة وقابلة للاختبار. يمكن اختبار كل دالة في خط الأنابيب بشكل منفصل.