البرمجة الوظيفية لمطوري Android - الجزء الأول

في الآونة الأخيرة ، أمضيت الكثير من الوقت في تعلم Elixir ، وهي لغة برمجة وظيفية رائعة وممتعة للمبتدئين.

هذا جعلني أفكر: لماذا لا تستخدم بعض المفاهيم والتقنيات من العالم الوظيفي في برمجة Android؟

عندما يسمع معظم الناس مصطلح البرمجة الوظيفية ، فإنهم يفكرون في منشورات Hacker News التي تتحدث عن Monads ووظائف الترتيب الأعلى وأنواع البيانات المجردة. يبدو أنه عالم صوفي بعيد كل البعد عن متاعب المبرمج اليومي ، وهو مخصص فقط لأقوى المتسللين المنحدرين من مملكة نيمنور.

حسنًا ، تبا لهذا ! أنا هنا لأخبرك أنه يمكنك أيضًا تعلمها. أنت أيضا يمكنك استخدامه. يمكنك أيضًا إنشاء تطبيقات جميلة باستخدامه. التطبيقات التي تحتوي على قواعد أكواد أنيقة وقابلة للقراءة وتحتوي على أخطاء أقل.

مرحبًا بك في البرمجة الوظيفية (FP) لمطوري Android. في هذه السلسلة ، سنتعلم أساسيات FP وكيف يمكننا استخدامها في Java القديمة الجيدة و Kotlin الجديدة الرائعة. الفكرة هي الحفاظ على المفاهيم متجذرة في التطبيق العملي وتجنب أكبر قدر ممكن من المصطلحات الأكاديمية.

FP هو موضوع ضخم. سنتعلم فقط المفاهيم والتقنيات المفيدة لكتابة كود Android. قد نقوم بزيارة بعض المفاهيم التي لا يمكننا استخدامها مباشرة من أجل الاكتمال ولكن سأحاول الحفاظ على المواد ذات الصلة قدر الإمكان.

جاهز؟ لنذهب.

ما هي البرمجة الوظيفية ولماذا يجب علي استخدامها؟

سؤال جيد. مصطلح البرمجة الوظيفية عبارة عن مظلة لمجموعة من مفاهيم البرمجة التي لا ينصفها اللقب. في انها الأساسية، إنها أسلوب البرمجة أن برامج يعامل تقييم وظائف حسابية والابتعاد عن دولة قابلة للتغيير و الآثار الجانبية (سوف نتحدث عن هذه قريبا بما فيه الكفاية).

تؤكد FP في جوهرها:

  • الكود التعريفي - يجب أن يقلق المبرمجون بشأن ما يجب أن يتركوا للمجمع ووقت التشغيل القلق بشأن الكيفية .
  • الوضوح يجب أن يكون الرمز كما هو واضح ممكن -. على وجه الخصوص ، الآثار الجانبيةيجب عزلها لتجنب المفاجآت. يتم تعريف تدفق البيانات ومعالجة الأخطاء بشكل واضح ويتم تجنب التركيبات مثل عبارات GOTO والاستثناءات لأنها يمكن أن تضع تطبيقك في حالات غير متوقعة.
  • التزامن - معظم الأكواد الوظيفية متزامنة بشكل افتراضي بسبب مفهوم يعرف باسم النقاء الوظيفي . يبدو أن الاتفاق العام هو أن هذه السمة على وجه الخصوص تتسبب في زيادة شعبية البرمجة الوظيفية لأن نوى وحدة المعالجة المركزية لا تزداد سرعة كل عام كما اعتادوا (انظر قانون مور) وعلينا أن نجعل برامجنا أكثر تزامنًا للاستفادة منها من البنى متعددة النواة.
  • وظائف الرتبة الأعلى - الدوال هي أعضاء من الدرجة الأولى تمامًا مثل كل اللغات الأساسية الأخرى. يمكنك تمرير الدوال تمامًا كما تفعل مع سلسلة نصية أو عدد صحيح.
  • الثبات - لا يتم تعديل المتغيرات بمجرد أن تتم تهيئتها. بمجرد أن يتم إنشاء شيء ما ، فهو ذلك الشيء إلى الأبد إذا كنت تريد أن يتغير ، فأنت تنشئ شيئًا جديدًا. هذا جانب آخر من جوانب الصراحة وتجنب الآثار الجانبية. إذا كنت تعلم أن شيئًا ما لا يمكن أن يتغير ، فلديك ثقة أكبر في حالته عند استخدامه.

التعليمات البرمجية التصريحية والصريحة والمتزامنة التي يسهل التفكير فيها والمصممة لتجنب المفاجآت؟ آمل أن أكون قد أثارت اهتمامك.

في هذا الجزء الأول من هذه السلسلة، لنبدأ مع بعض من أكثر المفاهيم الأساسية في FP: الطهارة ، الآثار الجانبية و الترتيب .

وظائف نقية

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

ضع في اعتبارك هذه الوظيفة البسيطة التي تضيف رقمين. يقرأ رقمًا واحدًا من ملف ويتم تمرير الرقم الآخر كمعامل.

جافا

int add(int x) { int y = readNumFromFile(); return x + y;}

كوتلن

fun add(x: Int): Int { val y: Int = readNumFromFile() return x + y}

إخراج هذه الوظيفة لا يعتمد فقط على مدخلاتها. اعتمادًا على ما يُرجع readNumFromFile () ، يمكن أن يكون لها مخرجات مختلفة لنفس قيمة x . يقال أن هذه الوظيفة غير نقية .

فلنحولها إلى دالة نقية.

جافا

int add(int x, int y) { return x + y;}

كوتلن

fun add(x: Int, y: Int): Int { return x + y}

الآن ناتج الوظيفة يعتمد فقط على مدخلاته. بالنسبة إلى x و y ، ستعيد الدالة دائمًا نفس الناتج. يقال الآن أن هذه الوظيفة نقية . تعمل الوظائف الرياضية أيضًا بنفس الطريقة. يعتمد ناتج الوظائف الرياضية فقط على مدخلاته - وهذا هو السبب في أن البرمجة الوظيفية أقرب إلى الرياضيات من أسلوب البرمجة المعتاد الذي اعتدنا عليه.

ملاحظة: لا يزال الإدخال الفارغ مدخلاً. إذا لم تأخذ الدالة أي مدخلات وتعيد نفس الثابت في كل مرة ، فإنها تظل نقية.

PPS تُعرف خاصية إرجاع نفس الإخراج دائمًا لمدخل معين أيضًا باسم الشفافية المرجعية وقد ترى أنها تستخدم عند الحديث عن وظائف نقية.

آثار جانبية

دعنا نستكشف هذا المفهوم بنفس مثال دالة الجمع. سنقوم بتعديل وظيفة الإضافة لكتابة النتيجة أيضًا إلى ملف.

جافا

int add(int x, int y) { int result = x + y; writeResultToFile(result); return result;}

كوتلن

fun add(x: Int, y: Int): Int { val result = x + y writeResultToFile(result) return result}

تقوم هذه الوظيفة الآن بكتابة نتيجة الحساب إلى ملف. أي أنه الآن يعدل حالة العالم الخارجي. يقال الآن أن هذه الوظيفة لها آثار جانبية ولم تعد وظيفة خالصة.

أي رمز يعدل حالة العالم الخارجي - يغير متغيرًا ، أو يكتب في ملف ، أو يكتب في قاعدة بيانات ، أو يحذف شيئًا ، وما إلى ذلك - يُقال إن له تأثيرًا جانبيًا.

يتم تجنب الوظائف التي لها آثار جانبية في FP لأنها لم تعد نقية وتعتمد على السياق التاريخي . سياق الكود ليس محتويا بذاته. هذا يجعلهم أكثر صعوبة في التفكير.

لنفترض أنك تكتب جزءًا من التعليمات البرمجية يعتمد على ذاكرة تخزين مؤقت. الآن يعتمد إخراج الكود الخاص بك على ما إذا كان شخص ما قد كتب إلى ذاكرة التخزين المؤقت ، وما هو مكتوب فيها ، ومتى تمت كتابته ، وما إذا كانت البيانات صالحة وما إلى ذلك. لا يمكنك فهم ما يفعله برنامجك ما لم تفهم جميع الحالات الممكنة من ذاكرة التخزين المؤقت التي يعتمد عليها. إذا قمت بتوسيع هذا ليشمل جميع الأشياء الأخرى التي يعتمد عليها تطبيقك - الشبكة وقاعدة البيانات والملفات وإدخال المستخدم وما إلى ذلك ، يصبح من الصعب جدًا معرفة ما يجري بالضبط وتناسب كل ذلك في ذهنك مرة واحدة.

هل هذا يعني أننا لا نستخدم الشبكات وقواعد البيانات وذاكرة التخزين المؤقت بعد ذلك؟ بالطبع لا. في نهاية التنفيذ ، تريد أن يقوم التطبيق بشيء ما. في حالة تطبيقات Android ، فهذا يعني عادةً تحديث واجهة المستخدم بحيث يمكن للمستخدم الحصول على شيء مفيد من تطبيقنا.

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

ترتيب

إذا كانت لدينا مجموعة من الوظائف البحتة التي ليس لها آثار جانبية ، فإن الترتيب الذي يتم تنفيذها به يصبح غير ذي صلة.

لنفترض أن لدينا وظيفة تستدعي 3 وظائف نقية داخليًا:

جافا

void doThings() { doThing1(); doThing2(); doThing3();}

كوتلن

fun doThings() { doThing1() doThing2() doThing3()}

نحن نعلم على وجه اليقين أن هذه الوظائف لا تعتمد على بعضها البعض (نظرًا لأن مخرجات أحدها ليست مدخلات أخرى) ونعلم أيضًا أنها لن تغير أي شيء في النظام (لأنها نقية). هذا يجعل الترتيب الذي يتم تنفيذه به قابلاً للتبادل تمامًا.

يمكن إعادة ترتيب ترتيب التنفيذ عشوائيًا وتحسينه للوظائف الصرفة المستقلة. لاحظ أنه إذا دخل doThing2 () جاءت نتيجة doThing1 () ثم هذه يجب أن يتم تنفيذها في النظام، ولكن doThing3 () يمكن أن يكون لا يزال لتنفيذ قبل بأمر إعادة doThing1 ().

ماذا يحصل لنا هذا الطلب على الرغم من؟ التزامن ، هذا ما! يمكننا تشغيل هذه الوظائف على 3 نوى منفصلة لوحدة المعالجة المركزية دون القلق بشأن إفساد أي شيء!

في كثير من الحالات ، يمكن للمترجمين في اللغات الوظيفية البحتة المتقدمة مثل Haskell معرفة ما إذا كانت الشفرة متزامنة بشكل رسمي أم لا ، ويمكن أن تمنعك من إطلاق النار على نفسك بسبب الجمود وظروف السباق وما شابه. يمكن لهؤلاء المترجمين نظريًا أيضًا إجراء موازاة تلقائية لشفرتك (هذا غير موجود بالفعل في أي مترجم أعرفه في الوقت الحالي ولكن البحث مستمر).

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

ملخص

آمل أن يكون هذا الجزء الأول قد أثار اهتمامك بشأن FP. تعمل الوظائف الخالية من التأثيرات الجانبية على تسهيل التفكير في الكود وهي الخطوة الأولى لتحقيق التزامن.

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

اقرأ التالي

البرمجة الوظيفية لمطوري Android - الجزء الثاني

إذا لم تكن قد قرأت الجزء الأول ، فيرجى قراءته هنا: medium.com

إذا أعجبك هذا ، انقر فوق؟ أدناه. ألاحظ كل واحد وأنا ممتن لكل واحد منهم.

لمزيد من التأملات حول البرمجة ، تابعوني حتى يتم إخطاري عندما أكتب منشورات جديدة.