البرمجة

تقنية Currying في جافاسكربت

تقنية Currying في جافاسكربت: المفهوم، التطبيق، والفائدة العميقة

تُعد تقنية Currying في لغة JavaScript من الأدوات البرمجية المتقدمة التي تمكّن المطورين من كتابة شيفرة أكثر مرونة، وقابلة لإعادة الاستخدام والتوسعة، وتسهّل تطبيق مبادئ البرمجة الوظيفية (Functional Programming). على الرغم من أن هذا المفهوم قد يبدو نظريًا في بدايته، فإنّ فهمه وتطبيقه يُعدّ خطوة جوهرية نحو كتابة شيفرة أنظف وأكثر كفاءة.

هذا المقال يعرض شرحًا موسّعًا ومعمّقًا لتقنية Currying، من حيث المفهوم الأساسي، وكيفية تنفيذها في JavaScript، والفرق بينها وبين التقنيات المشابهة، إلى جانب أهميتها العملية في بناء برامج أكثر مرونة واستدامة.


تعريف تقنية Currying

Currying هو عملية تحويل دالة تأخذ أكثر من معامل (arguments) إلى سلسلة من الدوال، كل منها تأخذ معاملًا واحدًا فقط وتُرجع دالة أخرى إلى أن يتم تمرير كل المعاملات المطلوبة، وعندها تُنفذ الدالة الأصلية.

بمعنى آخر، بدلاً من أن تأخذ دالة مثلًا ثلاثة معلمات دفعة واحدة:

javascript
function add(x, y, z) { return x + y + z; }

يتم تحويلها إلى:

javascript
function curriedAdd(x) { return function(y) { return function(z) { return x + y + z; } } }

الدالة curriedAdd تأخذ معاملًا واحدًا وتُرجع دالة تأخذ المعامل الثاني، والتي بدورها تُرجع دالة تأخذ المعامل الثالث وتعيد النتيجة النهائية.


الأصل الرياضي لتقنية Currying

مصطلح “Currying” مشتق من اسم عالم المنطق والرياضيات Haskell Curry، الذي ساهم في تطوير نظريات البرمجة الوظيفية. تُستخدم هذه التقنية في لغات مثل Haskell وScala وLisp بشكل جوهري، وقد تبنت JavaScript مفاهيم عديدة من هذه اللغات لدعم النمط الوظيفي.


الفرق بين Currying وPartial Application

رغم أن تقنية Currying تشبه تطبيقًا جزئيًا (Partial Application)، إلا أن هناك فرقًا جوهريًا بينهما:

المعيار Currying Partial Application
التعريف تحويل دالة متعددة المعاملات إلى سلسلة من دوال أحادية المعامل تثبيت عدد معين من المعاملات لدالة متعددة المعاملات
عدد الدوال المُنتَجة سلسلة من الدوال دالة جديدة بعد تثبيت بعض المعاملات
المرونة أكبر في التحكم بالمعاملات أقل نسبيًا

في Partial Application، يمكنك “تثبيت” بعض المعاملات ثم استخدام الدالة الناتجة لاحقًا:

javascript
function add(x, y, z) { return x + y + z; } const partialAdd = add.bind(null, 2); partialAdd(3, 4); // 9

كيفية كتابة دوال باستخدام Currying في JavaScript

المثال الأول: الكتابة اليدوية

javascript
function multiply(x) { return function(y) { return function(z) { return x * y * z; } } } multiply(2)(3)(4); // الناتج: 24

المثال الثاني: دالة Currying عامة

في بعض الأحيان، قد تحتاج إلى تطبيق تقنية Currying بشكل ديناميكي، على دالة ذات عدد غير معروف من المعاملات. وهنا يمكن استخدام دالة curry لتطبيق التقنية بشكل عام:

javascript
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function(...next) { return curried.apply(this, args.concat(next)); } } } }

مثال على الاستخدام:

javascript
function sum(a, b, c) { return a + b + c; } const curriedSum = curry(sum); curriedSum(1)(2)(3); // 6

فوائد استخدام Currying في JavaScript

تقنية Currying ليست فقط تمرينًا أكاديميًا في البرمجة، بل لها فوائد عملية متعددة تعزز من جودة الشيفرة وكفاءتها.

1. القابلية لإعادة الاستخدام (Reusability)

يمكن إنشاء دوال أكثر تحديدًا انطلاقًا من دالة عامة عبر تمرير بعض المعاملات مسبقًا:

javascript
const multiply = x => y => x * y; const double = multiply(2); double(5); // 10

2. تحسين الوضوح والنية (Clarity)

عند استخدام Currying، يمكن أن تكون الشيفرة أكثر وضوحًا وتعبر عن النية البرمجية بشكل أدق، خاصة عند كتابة دوال مخصصة جزئيًا لأغراض معينة.

3. التوافق مع البرمجة الوظيفية

تقنية Currying تسهّل التركيب بين الدوال (Function Composition)، وهي خاصية مركزية في البرمجة الوظيفية، ما يجعل من السهل ربط دوال صغيرة معًا لتكوين منطق أكبر وأكثر تعقيدًا.


Currying في مكتبات JavaScript الشائعة

العديد من مكتبات JavaScript مثل Lodash وRamda تدعم تقنية Currying بشكل مدمج.

Lodash:

javascript
const _ = require('lodash'); function volume(length, width, height) { return length * width * height; } const curriedVolume = _.curry(volume); curriedVolume(2)(3)(4); // 24

Ramda:

javascript
const R = require('ramda'); const volume = (l, w, h) => l * w * h; const curriedVolume = R.curry(volume); curriedVolume(2)(3)(4); // 24

تسهل هذه المكتبات تطبيق البرمجة الوظيفية من خلال توفير دوال مهيأة تلقائيًا بتقنية Currying.


مقارنة بين شيفرة تقليدية وشيفرة باستخدام Currying

الحالة الشيفرة التقليدية الشيفرة باستخدام Currying
تعريف دالة جمع function add(x, y) { return x + y; } const add = x => y => x + y;
الاستخدام المباشر add(2, 3) add(2)(3)
إنشاء دالة جديدة من دالة موجودة يتطلب استخدام bind أو دالة وسيطة مباشرة: const addFive = add(5)

استخدام Currying في نماذج واقعية

التحقق من الصلاحيات

javascript
const checkPermission = role => action => { const permissions = { admin: ['create', 'delete'], user: ['read'] }; return permissions[role].includes(action); }; const isAdmin = checkPermission('admin'); isAdmin('create'); // true

إعدادات واجهات المستخدم (UI Settings)

javascript
const setStyle = property => value => element => { element.style[property] = value; }; // إعداد دالة لتغيير اللون const setColor = setStyle('color'); const setRed = setColor('red'); setRed(document.querySelector('h1'));

حالات يُفضل فيها استخدام Currying

  • عندما ترغب في تخصيص دالة عامة لاستخدامها في سياق معيّن.

  • عندما تتعامل مع دوال منسّقة مع map أو filter أو reduce.

  • عند استخدام مبدأ composition لبناء دوال أكثر تعقيدًا من وحدات بسيطة.


ملاحظات مهمة عند استخدام Currying

  • Currying لا يُناسب دائمًا جميع السيناريوهات، خاصة في الدوال التي تتطلب تمرير جميع المعاملات دفعة واحدة.

  • الأداء قد يتأثر في بعض التطبيقات الكبيرة إذا تم الإفراط في الاستخدام.

  • يجب مراعاة قابلية القراءة لدى الفريق البرمجي، حيث قد تكون الدوال المُكرّبة مربكة للمطورين غير المعتادين على النمط الوظيفي.


خريطة مقارنة مبسطة: Currying مقابل تقنيات أخرى

التقنية تعريفها مثال الميزة الأساسية
Currying تحويل دالة متعددة المعاملات إلى سلسلة من دوال أحادية f(a)(b)(c) التخصيص التدريجي للمعاملات
Partial Application تثبيت عدد من المعاملات لدالة f.bind(null, a) تسهيل تكرار استخدام الدالة بقيم ثابتة
Composition دمج دوال صغيرة في دالة أكبر compose(f, g)(x) إعادة استخدام منطق برمجي صغير مركب
Closures دوال تتذكر السياق الذي أُنشئت فيه function(x) { return y => x + y; } حفظ القيم داخل السياق الأصلي

الخلاصة

تقنية Currying تُعد من الركائز الأساسية للبرمجة الوظيفية، وقد أثبتت فائدتها في بناء برمجيات مرنة، واضحة، وسهلة الصيانة. في JavaScript، حيث تتقاطع البرمجة الكائنية مع الوظيفية، تُوفّر Currying أداة قوية لتعزيز أنماط برمجية متقدمة تساعد على إعادة استخدام الشيفرة بشكل فعال وتقليل التعقيد في التعامل مع المعاملات المتعددة. سواء كنت تطوّر مكتبة، أو تبني واجهة تفاعلية معقدة، فإن تبنّي هذه التقنية قد يكون الفارق بين شيفرة عشوائية وأخرى أنيقة.


المراجع:

  1. Kyle Simpson – You Don’t Know JS Yet (https://github.com/getify/You-Dont-Know-JS)

  2. Ramda Documentation – https://ramdajs.com/docs/#curry