دليل البقاء على قيد الحياة لأي واحد في سكالا
بدأت العمل مع سكالا منذ بضعة أشهر. أحد المفاهيم التي واجهت أكثر صعوبة في فهمها هو 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
و انتهينا. كانت هذه هي الحالة التي لم يُرجع فيها حسابنا Error
a داخل Left
.
إذا حدث هذا ، فهذا يعني أنه حدث خطأ ما في حساباتنا ، لذا يمكننا إرجاع Left
قيمة Error
. لدينا المنطق ، والآن نحتاج إلى الكود.
خريطة
العمل Map
بسيط للغاية بمجرد أن ننتهي من الواجب المنزلي لـ List
(على الرغم من الحاجة إلى جعله عامًا):
- الخطوة الأولى: تحويل
Map
في aList
من الذيEither
يحتوي على المجموعة (مفتاح ، قيمة). - الخطوة الثانية: مرر النتيجة إلى الوظيفة التي حددناها
List
. - الخطوة الثالثة: قم بتحويل
List
المجموعات داخلEither
aMap
.
سهل جدا.
لنكن أنيقًا: محول ضمني مفيد
قدمنا 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 ، لذلك أفضل القراءة على التعقيد (الأنيق).
إذا كان لديك حل أفضل ، خاصة بالنسبة لفئة المرافق ، سأكون سعيدًا برؤيته وتعلم شيء جديد! :-)