دليل البقاء على قيد الحياة لأي واحد في سكالا

بدأت العمل مع سكالا منذ بضعة أشهر. أحد المفاهيم التي واجهت أكثر صعوبة في فهمها هو Eitherالموناد. لذلك ، قررت أن أتلاعب بها وأفهم قوتها بشكل أفضل.

في هذه القصة أشارك ما تعلمته ، على أمل مساعدة المبرمجين في التعامل مع هذه اللغة الجميلة.

إما أحادي

Eitherهي واحدة من أكثر المونادات فائدة في سكالا. إذا كنت تتساءل ما هو monad ، حسنًا ... لا يمكنني الخوض في التفاصيل هنا ، ربما في قصة مستقبلية!

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

في هذه الحالة المحددة ، Eitherيمكن أن يكون لمربعنا "نموذجان". يمكن أن يكون (إما) Leftأو أ Right، اعتمادًا على نتيجة الحساب بداخله.

يمكنني سماعك تسأل: "حسنًا ، وما هي فائدتها؟"

الجواب المعتاد هو: معالجة الأخطاء.

يمكننا أن نضع حسابًا في Either، ونجعله Leftفي حالة وجود أخطاء ، أو Rightيحتوي على نتيجة في حالة النجاح. استخدام Leftالأخطاء Rightوالنجاح اصطلاح. دعونا نفهم هذا ببعض التعليمات البرمجية!

في هذا المقتطف نقوم بتعريف Eitherمتغير فقط .

يمكننا تعريفه على أنه Rightيحتوي على قيمة صالحة ، أو Leftيحتوي على خطأ. لدينا أيضًا عملية حسابية تُرجع Either، مما يعني أنه يمكن أن يكون a Leftأو a Right. بسيط ، أليس كذلك؟

الإسقاط الأيمن والأيسر

بمجرد أن نحصل على الحساب في المربع ، قد نرغب في الحصول على القيمة منه. أنا متأكد من أنك تتوقع أن استدعاء .getعلى Eitherواستخراج النتيجة.

هذا ليس بهذه البساطة.

فكر في الأمر: لقد وضعت الحساب الخاص بك في Either، لكنك لا تعرف ما إذا كان نتج Leftعنه Right. إذن ماذا يجب أن .getتعود المكالمة؟ الخطأ أم القيمة؟

هذا هو السبب في الحصول على النتيجة ، يجب أن تضع افتراضًا حول نتيجة الحساب.

هنا يأتي دور الإسقاط .

بدءًا من Either، يمكنك الحصول RightProjectionعلى LeftProjection. الأول يعني أنك تفترض أن الحساب أدى إلى a Right، والثاني في a Left.

أعلم ، أعلم ... قد يكون هذا محيرًا بعض الشيء. من الأفضل فهمه ببعض التعليمات البرمجية. بعد كل شيء ، الكود دائما يقول الحقيقة .

هذا هو. لاحظ أنه عند محاولتك الحصول على النتيجة من a RightProjection، لكنها a Left، تحصل على استثناء. الشيء نفسه ينطبق على LeftProjectionولديك ملف Right.

الشيء الرائع هو أنه يمكنك تعيين الإسقاطات. هذا يعني أنه يمكنك أن تقول: "افترض أنه حق: افعل ذلك به" ، وترك الحق بدون Leftتغيير (والعكس صحيح ).

من الخيار إلى أي منهما

Option هي طريقة شائعة أخرى للتعامل مع القيم غير الصالحة.

و Optionيمكن أن يكون لها قيمة أو تكون فارغة (انها القيمة Nothing). أراهن أنك لاحظت وجود تشابه مع Either… إنه أفضل ، لأنه يمكننا في الواقع تحويل ال Optionإلى Either! وقت البرمجة!

من الممكن تحويل Optionإلى a Leftأو a Right. Eitherسيحتوي الجانب الناتج من الإرادة على قيمة Optionإذا تم تعريفه. رائع. انتظر لحظة ... ماذا لو كانت Optionفارغة؟ نحصل على الجانب الآخر ، لكننا نحتاج إلى تحديد ما نتوقع أن نجد فيه.

بالداخل بالخارج

Eitherهو سحر ، نتفق جميعًا على ذلك. لذلك قررنا استخدامه لحساباتنا غير المؤكدة. السيناريو النموذجي عند القيام بالبرمجة الوظيفية هو تعيين دالة على a Listمن العناصر ، أو على a Map. لنفعل ذلك Eitherبحساباتنا الجديدة ذات القوة الجديدة ...

Huston ، لدينا "مشكلة" (حسنًا ، إنها ليست مشكلة كبيرة ، لكنها غير مريحة بعض الشيء). سيكون من الأفضل أن يكون لديك المجموعة داخل Eitherالكثير من Eitherداخل المجموعة. يمكننا العمل على ذلك.

قائمة

لنبدأ مع List. أولاً نفكر في ذلك ، ثم يمكننا اللعب بالشفرة.

علينا استخراج القيمة من Either، ووضعها في List، ووضع القائمة داخل ملف Either. جيد، أعجبني.

النقطة المهمة هي أنه يمكننا الحصول على a Leftأو a Right، لذلك نحن بحاجة للتعامل مع كلتا الحالتين. حتى نجد قيمة a Right، يمكننا وضع قيمتها داخل a جديد List. نواصل بهذه الطريقة نراكم كل قيمة في الجديد List.

في نهاية المطاف سنصل نهاية Listلل Either، وهذا يعني لدينا جديدة Listتحتوي على جميع القيم. يمكننا حزمه في Rightو انتهينا. كانت هذه هي الحالة التي لم يُرجع فيها حسابنا Errora داخل Left.

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

خريطة

العمل Mapبسيط للغاية بمجرد أن ننتهي من الواجب المنزلي لـ List(على الرغم من الحاجة إلى جعله عامًا):

  • الخطوة الأولى: تحويل Mapفي a Listمن الذي Eitherيحتوي على المجموعة (مفتاح ، قيمة).
  • الخطوة الثانية: مرر النتيجة إلى الوظيفة التي حددناها List.
  • الخطوة الثالثة: قم بتحويل Listالمجموعات داخل Eithera Map.

سهل جدا.

لنكن أنيقًا: محول ضمني مفيد

قدمنا Eitherوفهمنا أنه مفيد للتعامل مع الأخطاء. لعبنا قليلا مع التوقعات. رأينا كيف ننتقل من an Optionإلى Either. قمنا أيضًا بتنفيذ بعض الوظائف المفيدة "للاستخراج" Eitherمن Listو Map. حتى الان جيدة جدا.

أود أن أنهي رحلتنا في Eitherالموناد قليلاً. تؤدي وظائف المرافق التي حددناها وظائفهم ، لكني أشعر أن شيئًا ما مفقود ...

سيكون من المدهش أن نقوم بالتحويل مباشرة على المجموعة. سيكون لدينا شيء مثل myList.toEitherListأو myMap.toEitherMap. أكثر أو أقل مثل ما نفعله مع Option.toRightأو Option.toLeft.

بشرى سارة: يمكننا فعل ذلك باستخدام الفئات الضمنية !

يتيح لنا استخدام الفئات الضمنية في Scala توسيع قدرات فئة أخرى.

في حالتنا ، نقوم بتوسيع القدرة Listو Map"استخراج" ملف Either. تنفيذ التحويل هو نفسه الذي حددناه من قبل. الاختلاف الوحيد هو أننا الآن نجعلها عامة. أليس سكالا رائعًا؟

نظرًا لأن هذا يمكن أن يكون فئة أدوات مفيدة ، فقد أعددت لك فكرة يمكنك نسخها ولصقها بسهولة.

object EitherConverter { implicit class EitherList[E, A](le: List[Either[E, A]]){ def toEitherList: Either[E, List[A]] = { def helper(list: List[Either[E, A]], acc: List[A]): Either[E, List[A]] = list match { case Nil => Right(acc) case x::xs => x match { case Left(e) => Left(e) case Right(v) => helper(xs, acc :+ v) } } helper(le, Nil) } } implicit class EitherMap[K, V, E](me: Map[K, Either[E, V]]) { def toEitherMap: Either[E, Map[K, V]] = me.map{ case (k, Right(v)) => Right(k, v) case (_, e) => e }.toList.toEitherList.map(l => l.asInstanceOf[List[(K, V)]].toMap) } }

استنتاج

هذا كل ما لدي أيها الناس. آمل أن تساعدك هذه القصة القصيرة على فهم Eitherالأحادي بشكل أفضل .

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

إذا كان لديك حل أفضل ، خاصة بالنسبة لفئة المرافق ، سأكون سعيدًا برؤيته وتعلم شيء جديد! :-)