تأثير ZIO والقطط: تحالف ناجح

أصبح Cats Effect نوعًا من "التدفقات التفاعلية" من أجل عالم Scala الوظيفي ، مما يتيح لك الجمع بين النظام البيئي المتنوع بالكامل للمكتبات معًا.

يتم تنفيذ العديد من المكتبات الممتازة: http4s ، fs2 ، doobie - فقط على أساس فئات الكتابة من Cats Effect. وتوفر المكتبات ، مثل ZIO و Monix ، بدورها ، مثيلات لفئات الأنواع هذه لأنواع تأثيرها. على الرغم من بعض المشكلات التي سيتم إصلاحها في الإصدار 3.0 ، يساعد Cats Effect العديد من المساهمين في المصادر المفتوحة على دعم عضويا للنظام الإيكولوجي الوظيفي بالكامل للغة Scala. يواجه المطورون الذين يستخدمون Cats Effect خيارًا صعبًا: أي تطبيق للآثار لاستخدامه في تطبيقاتهم.

اليوم هناك ثلاثة بدائل:

  • القطط IO ، مرجع التنفيذ ؛
  • Monix ، نوع بيانات المهمة وتفاعلها في التعليمات البرمجية ؛
  • ZIO ، نوع بيانات ZIO ونطاق ترابطها.

سأحاول في هذا المنشور أن أثبت لك أنه لإنشاء تطبيقك باستخدام Cats Effect ، يعد ZIO اختيارًا جيدًا مع حلول وقدرات تصميم مختلفة تمامًا عن تنفيذ المراجع في Cats IO.

1. أفضل MTL / الهندسة النهائية بدون علامات


MTL (مكتبة محولات الموناد) هي نمط برمجة تكون فيه الوظائف متعددة الأشكال حسب نوع تأثيرها وتعبر عن متطلباتها من خلال "قيد فئة الكتابة". في سكالا ، يُطلق عليه غالبًا النمط النهائي بدون علامات (على الرغم من أنه ليس نفس الشيء) ، خاصةً عندما لا تحتوي قوانين النوع على قوانين.

من المعروف أنه لا يمكن تحديد مثيل عمومي لفئات أنواع MTL الكلاسيكية مثل Writer و State ، وكذلك لأنواع التأثيرات مثل Cats IO. المشكلة هي أن مثيلات هذه الأنواع من الأنواع لهذه الأنواع من التأثيرات تتطلب الوصول إلى حالة قابلة للتغيير ، والتي لا يمكن إنشاؤها عالميًا ، لأن إنشاء حالة قابلة للتغيير هو أيضًا تأثير.

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

لتحقيق ذلك ، يستخدم مبرمجو Scala خدعة: يقومون بإنشاء مثيلات (ولكن نظيفة) في المستوى العلوي من برامجهم مع التأثيرات ثم يزودونهم بالمزيد في البرنامج كتأثيرات محلية:

Ref.make[AppState](initialAppState).flatMap(ref => implicit val monadState = new MonadState[Task, AppState] { def get: Task[AppState] = ref.get def set(s: AppState): Task[Unit] = ref.set(s).unit } myProgram ) 

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

هناك ميزة رائعة في MTL / tagless-final وهي أنه يمكنك تحديد معظم الحالات مباشرة أعلى نوع بيانات ZIO باستخدام بيئة ZIO.

فيما يلي طريقة واحدة لإنشاء تعريف MonadState عالمي لنوع بيانات ZIO:

 trait State[S] { def state: Ref[S] } implicit def ZIOMonadState[S, R <: State[S], E]: MonadState[ZIO[R, E, ?], S] = new MonadState[ZIO[R, E, ?], S] { def get: ZIO[R, E, S] = ZIO.accessM(_.state.get) def set(s: S): ZIO[R, E, Unit] = ZIO.accessM(_.state.set(s).unit) } 

يتم الآن تعريف مثيل عالميًا لأي بيئة تدعم الحالة على الأقل State[S] .

وبالمثل بالنسبة لـ FunctorListen ، والمعروف باسم MonadWriter :

 trait Writer[W] { def writer: Ref[W] } implicit def ZIOFunctorListen[W: Semigroup, R <: Writer[W], E]: FunctorListen[ZIO[R, E, ?], W] = new FunctorListen[ZIO[R, E, ?], W] { def listen[A](fa: ZIO[R, E, A]): ZIO[R, E, (A, W)] = ZIO.accessM(_.state.get.flatMap(w => fa.map(a => a -> w))) def tell(w: W): ZIO[R, E, W] = ZIO.accessM(_.state.update(_ |+| w).unit) } 

وبالطبع ، يمكننا أن نفعل الشيء نفسه مع MonadError :

 implicit def ZIOMonadError[R, E]: MonadError[ZIO[R, E, ?], E] = new MonadError[ZIO[R, E, ?], E]{ def handleErrorWith[A](fa: ZIO[R, E, A])(f: E => ZIO[R, E, A]): ZIO[R, E, A] = fa catchAll f def raiseError[A](e: E): ZIO[R, E, A] = ZIO.fail(e) } 

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

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

2. توفير الموارد لمجرد البشر


كانت إحدى الميزات الأولى لـ ZIO هي interraption - قدرة وقت تشغيل ZIO على مقاطعة أي تأثير قابل للتنفيذ على الفور ومضمونة لتحرير جميع الموارد. تطبيق الخام لهذه الميزة ضرب القطط IO.

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

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

النظر في التأثير التالي:

 for { handle <- openFile(file) data <- readFile(handle) _ <- closeFile(handle) } yield data 

لن يتفاجأ معظم المطورين بهذا السيناريو: لن يتم تنفيذ readFile حالة تعطل readFile . لحسن الحظ ، فإن نظام التأثيرات لديه ensuring ( guarantee في Cats Effect) يسمح لك بإضافة معالج نهائي إلى تأثير finalizer ، على غرار النهائي.

لذلك ، يمكن حل المشكلة الرئيسية في الكود أعلاه:

 for { handle <- openFile(file) data <- readFile(handle).ensuring(closeFile(handle)) } yield () 

أصبح التأثير الآن "مقاومًا للسقوط" ، بمعنى أنه إذا readFile ملف readFile ، readFile الملف مغلقًا. وإذا نجح readFile ، فسيتم إغلاق الملف أيضًا. في جميع الحالات ، سيتم إغلاق الملف.

ولكن لا يزال غير تماما على الإطلاق. المقاطعة تعني أنه يمكن مقاطعة التأثير في كل مكان ، حتى بين openFile و readFile . إذا حدث هذا ، فلن يتم إغلاق الملف المفتوح وسيحدث تسرب للموارد.

نمط الحصول على مورد وإطلاقه منتشر على نطاق واسع لدرجة أن ZIO قدمت مشغل قوس ، والذي ظهر أيضًا في Cats Effect 1.0. تحمي عبارة Bracket من الانقطاعات: إذا تم تلقي المورد بنجاح ، فسيحدث الإصدار حتى إذا تمت مقاطعة التأثير باستخدام المورد. علاوةً على ذلك ، لا يمكن مقاطعة ولا استلام المورد ، مما يوفر ضمانًا لأمان المورد.

باستخدام شريحة ، يبدو المثال أعلاه كما يلي:

 openFile(file).bracket(closeFile(_))(readFile(_)) 

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

بشكل عام ، كل عمل المقاطعة ينقسم إلى شيئين رئيسيين:

  • منع الانقطاعات في بعض المناطق التي قد تنقطع ؛
  • السماح بالانقطاع في المناطق التي قد تتجمد.

ZIO لديه القدرة على تنفيذ كليهما. على سبيل المثال ، يمكننا تطوير نسختنا الخاصة من الأقواس باستخدام تجريدات ZIO منخفضة المستوى:

 ZIO.uninterruptible { for { a <- acquire exit <- ZIO.interruptible(use(a)) .run.flatMap(exit => release(a, exit) .const(exit)) b <- ZIO.done(exit) } yield b } 

في هذا الرمز ، يعد use(a) هو الجزء الوحيد الذي يمكن مقاطعته. يضمن القانون المحيط تنفيذ release في أي حال.

في أي وقت ، يمكنك التحقق مما إذا كانت هناك فرصة للمقاطعة. لهذا ، هناك حاجة فقط إلى عمليتين بدائيتين (كل الباقي مشتق منها).

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

يوفر Cats IO عملية واحدة فقط للتحكم في المقاطعات: Combinator unancelable. يجعل كتلة كاملة من التعليمات البرمجية دون انقطاع. على الرغم من أن هذه العملية نادراً ما يتم استخدامها ، إلا أنها قد تؤدي إلى تسرب أو تأمين المورد.

في الوقت نفسه ، اتضح أنه يمكنك تحديد بدائية داخل Cats IO ، والتي تتيح لك تحقيق المزيد من التحكم في المقاطعات. اتضح أن تنفيذ فابيو لابيلا المعقد للغاية كان بطيئًا للغاية.

يتيح لك ZIO كتابة التعليمات البرمجية مع الانقطاعات ، التي تعمل على مستوى عالٍ مع بيانات مركب إعلاني ، ولا يفرض عليك الاختيار بين التعقيد الشديد المقترن بالأداء المنخفض والتسرب المحظور.

علاوة على ذلك ، فإن ذاكرة Transactional Memory المضافة مؤخرًا في ZIO تتيح للمستخدم كتابة هياكل البيانات ورموزها بشكل غير متزامن وتنافسي تلقائيًا وتسمح بالمقاطعات.

3. مضمون النهائي


توفر كتلة try / وأخيراً في العديد من لغات البرمجة الضمانات اللازمة لكتابة التعليمات البرمجية المتزامنة دون تسرب الموارد.

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

ينطبق هذا الضمان على:

  • هناك كتل "حاول / أخيرًا" متداخلة ؛
  • توجد أخطاء في "try block" ؛
  • هناك أخطاء في كتلة المتداخلة أخيرا.

يمكن استخدام عملية "ضمان" ZIO تمامًا مثل المحاولة / أخيرًا:

 val effect2 = effect.ensuring(cleanup) 

توفر ZIO الضمانات التالية لـ "effect.ensuring (finalizer)": إذا بدأ تنفيذ "effect" ، فسيبدأ "finalizer" في التنفيذ عند توقف "effect".

مثل المحاولة / أخيرًا ، تبقى هذه الضمانات في الحالات التالية:

  • هناك تركيبات "مضمونة" متداخلة ؛
  • هناك أخطاء في "التأثير" ؛
  • هناك أخطاء في "finalizer" المتداخلة.

علاوة على ذلك ، يتم الحفاظ على الضمان حتى إذا تمت مقاطعة التأثير (تتشابه الضمانات على "شريحة" ، في الواقع ، يتم تنفيذ "قوس" على "ضمان").

يوفر نوع بيانات Cats IO ضمانًا آخر أضعف. بالنسبة إلى "effect.guarantee (finalizer)" ، يتم إضعافها على النحو التالي: إذا بدأ تنفيذ "effect" ، فسيبدأ "finalizer" في التنفيذ عند توقف "effect" ، إذا لم يتم إدراج تأثير المشكلة في "effect".

يوجد ضمان أضعف أيضًا في تنفيذ "القوس" في Cats IO.

للحصول على تسرب مورد ، ما عليك سوى استخدام التأثير المستخدم داخل تأثير "الكفالة" أو "bracket.use" ، وقم بتكوينه بشيء من هذا القبيل:

 //   `interruptedFiber` -    val bigTrouble = interruptedFiber.join 

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

على سبيل المثال ، لن يبدأ "finalizer" في التعليمة البرمجية التالية في التنفيذ:

 (IO.unit >> bigTrouble).guarantee(IO(println("Won't be executed!!!«))) 

عند تقييم الكود دون مراعاة السياق العام ، من المستحيل تحديد ما إذا كان سيتم إدراج تأثير ، مثل "bigTrouble" ، في أي مكان في تأثير "use" لعملية "bracket" أو داخل كتلة "finalizer".

لذلك ، لن تتمكن من معرفة ما إذا كان برنامج Cats IO سيعمل مع تسرب الموارد أو كتل "finalizer" المفقودة دون تقييم البرنامج بالكامل. لا يمكن تقييم البرنامج بالكامل إلا يدويًا ، وتكون هذه العملية دائمًا مصحوبة بأخطاء لا يمكن التحقق منها بواسطة المترجم. بالإضافة إلى ذلك ، يجب تكرار هذه العملية في كل مرة تحدث فيها أية تغييرات مهمة في الكود.

لدى ZIO تطبيق مخصص لـ "الضمان" من Cats Effect و "warrantyCase" و "bracket". تستخدم التطبيقات دلالات ZIO الأصلية (وليس دلالات Cats IO) ، مما يسمح لنا بتقييم المشاكل المحتملة مع تسرب الموارد هنا والآن ، مع العلم أنه في جميع المواقف سيتم إطلاق النهائي وسيتم تحرير الموارد.

4. التبديل مستقرة


يحتوي Cats Effect على طريقة "evalOn" من "ContextShift" ، والتي تتيح لك نقل تنفيذ بعض التعليمات البرمجية إلى سياق تنفيذ آخر.

هذا مفيد لعدة أسباب:

  • فرض العديد من مكتبات العميل عليك القيام ببعض العمل في تجمع مؤشرات الترابط الخاصة بهم؛
  • تتطلب مكتبات UI بعض التحديثات تحدث في مؤشر ترابط UI؛
  • تتطلب بعض التأثيرات عزلًا على تجمعات مؤشرات الترابط التي تم تكييفها مع ميزاتها المحددة.

تنفذ عملية "EvalOn" التأثير حيث يجب تشغيلها ، ثم تعود إلى سياق التنفيذ الأصلي. على سبيل المثال:

 cs.evalOn(kafkaContext)(kafkaEffect) 

ملاحظة: لدى Cats IO بنية "تحول" مماثلة ، والتي تسمح لك بالتبديل إلى سياق مختلف دون الرجوع إلى الخلف ، ولكن في الممارسة العملية ، نادرًا ما يكون هذا السلوك مطلوبًا ، لذلك يفضل "evalOn".

يوفر تطبيق ZIO لـ "evalOn" (المصنوع على "قفل" ZIO البدائي) الضمانات اللازمة لفهم مكان تأثير التأثير بشكل فريد - سيتم تنفيذ التأثير دائمًا في سياق محدد.

القطط IO لديها ضمان مختلف وأضعف - سيتم تنفيذ التأثير في سياق معين حتى أول عملية غير متزامنة أو التبديل الداخلي.

النظر في جزء صغير من التعليمات البرمجية ، فمن المستحيل معرفة ما إذا كان سيتم دمج تأثير غير متزامن (أو التبديل المتداخل) في التأثير الذي سيتم التبديل ، لأنه لا يتم عرض عدم التزامن في الأنواع.

لذلك ، كما هو الحال في أمان الموارد ، لفهم أين سيتم إطلاق تأثير Cats IO ، من الضروري دراسة البرنامج بأكمله. في الممارسة العملية ، ومن تجربتي ، فإن مستخدمي Cats IO يشعرون بالدهشة عندما ، عند استخدام "evalOn" في سياق واحد ، يكتشف لاحقًا أن معظم التأثير قد تم بطريق الخطأ في سياق آخر.

يتيح لك ZIO تحديد المكان الذي يجب تشغيل التأثيرات فيه ، والثقة في أنه سيحدث في جميع الحالات ، بغض النظر عن كيفية دمج التأثيرات في تأثيرات أخرى.

5. أمن رسائل الخطأ


سيتم تشغيل أي تأثير يدعم التزامن أو التزامن أو الوصول الآمن إلى الموارد في نموذج خطأ خطي: ​​بشكل عام ، لا يمكن حفظ جميع الأخطاء.

هذا صحيح بالنسبة لكل من "Throwable" ، وهو نوع خطأ ثابت مدمج في Cats IO ، ونوع الخطأ متعدد الأشكال المدعوم من ZIO.

أمثلة على المواقف التي بها عدة أخطاء لمرة واحدة:

  • Finalizer يلقي استثناء.
  • يتم الجمع بين اثنين (السقوط) الآثار في تنفيذ مواز ؛
  • اثنين (السقوط) الآثار في حالة السباق؛
  • يسقط التأثير المتقطع قبل مغادرة القسم المحمي من الانقطاعات.

نظرًا لعدم حفظ جميع الأخطاء ، توفر ZIO بنية بيانات "السبب [E]" استنادًا إلى شبه حلقة مجانية (تجريد من الجبر التجريدي ، ولا يُفترض معرفتها هنا) ، مما يسمح بتوصيل الأخطاء التسلسلية والمتوازية لأي نوع من الأخطاء. خلال جميع العمليات (بما في ذلك التنظيف للتأثير الساقط أو المتقطع) ، تقوم ZIO بتجميع الأخطاء في بنية بيانات "السبب [E]". هيكل البيانات هذا متاح في أي وقت. نتيجة لذلك ، تقوم ZIO دائمًا بتخزين جميع الأخطاء: فهي متوفرة دائمًا ، ويمكن تسجيلها ودراستها وتحويلها وفقًا لمتطلبات العمل.

اختار القطط IO نموذج مع فقدان معلومات الخطأ. بينما تقوم ZIO بتوصيل الخطأين من خلال Cause [E] ، فإن Cats IO "تفقد" إحدى رسائل الخطأ ، على سبيل المثال ، عن طريق استدعاء "e.printStackTrace ()" على الخطأ الذي يحدث.

على سبيل المثال ، سيتم فقد خطأ في "finalizer" في هذا الرمز.

 IO.raiseError(new Error("Error 1")).guarantee(IO.raiseError(new Error("Error 2«))) 

هذا النهج لتتبع الأخطاء يعني أنه لا يمكنك تحديد موقع ومعالجة مجموعة كاملة من الأخطاء التي تحدث بسبب مزيج من الآثار محليا. تسمح لك ZIO باستخدام أي نوع من الأخطاء ، بما في ذلك "Throwable" (أو أنواع فرعية أكثر تحديدًا مثل "IOExceptio" أو تسلسل هرمي استثناء آخر مخصص) ، مما يضمن عدم فقد أية أخطاء أثناء تنفيذ البرنامج.

6. تزامن دون الجمود


يوفر كل من ZIO و Cats IO مُنشئًا يسمح لك بتدوين الرمز مع رد اتصال ولفه في الواقع

يتم توفير هذه الميزة من خلال فئة أنابيب Async في تأثير القطط:

 val effect: Task[Data] = Async[Task].async(k => getDataWithCallbacks( onSuccess = v => k(Right(v)), onFailure = e => k(Left(e)) )) 

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

لاحظ أنه بمجرد أن يتحول رمز رد الاتصال إلى تأثير ، يتم استدعاء وظيفة رد الاتصال (تسمى هنا `k`). إنهاء وظيفة رد الاتصال هذه بقيمة نجاح / خطأ. عند استدعاء وظيفة رد الاتصال هذه ، يستأنف تنفيذ التأثير (تم إيقافه مؤقتًا مسبقًا).

تضمن ZIO أن التأثير سيستأنف التنفيذ على تجمع مؤشرات ترابط وقت التشغيل إذا لم يتم تعيين التأثير إلى أي سياق خاص معين ، أو إلى سياق آخر تم إرفاق التأثير به.

يستأنف القطط IO التأثير على مؤشر ترابط رد الاتصال. الفرق بين هذه الخيارات عميق: لا يتوقع مؤشر الترابط الذي يتسبب في رد الاتصال أن يتم تنفيذ رمز رد الاتصال إلى الأبد ، ولكنه يسمح فقط بتأخير بسيط قبل إرجاع عنصر التحكم. من ناحية أخرى ، لا تقدم Cats IO مثل هذا الضمان على الإطلاق: قد يتجمد مؤشر الترابط call call ، إطلاق callback ، في انتظار وقت غير محدد عند عودة التحكم في التنفيذ.

استأنفت الإصدارات السابقة من هياكل البيانات التنافسية في Cats Effect ("مؤجل" ، "Semaphore") الآثار التي لم تُرجع التحكم في التنفيذ إلى مؤشر الترابط المتصل. نتيجة لذلك ، تم اكتشاف المشاكل المتعلقة الجمود وجدولة التنفيذ مقطوعة فيها. على الرغم من أنه تم العثور على كل هذه المشكلات ، إلا أنها تم حلها فقط من أجل هياكل البيانات التنافسية في Cats Effect.

كود المستخدم الذي يستخدم طريقة مماثلة كما في Cats IO سوف يواجه مثل هذه المشاكل ، لأن مثل هذه المهام غير حتمية ، يمكن أن تحدث الأخطاء في حالات نادرة للغاية ، في وقت التشغيل ، مما يجعل تصحيح الأخطاء واكتشاف المشكلة عملية صعبة.

يوفر ZIO حماية حالة توقف تام ومجدولة مهمة عادية خارج الصندوق ، كما يجعل المستخدم يختار بوضوح سلوك Cats IO (على سبيل المثال ، باستخدام "unsafeRun" في "Promise" ، والتي انتهت بتأثير غير متزامن مستأنف).

على الرغم من أن أياً من الحلول غير مناسب لجميع الحالات ، وتوفر ZIO و Cats IO مرونة كافية لحل جميع المواقف (بطرق مختلفة) ، فإن اختيار ZIO يعني استخدام "Async" دون أي قلق ويفرض عليك وضع رمز المشكلة في "unsafeRun" ، وهو معروف بالتسبب في حالة توقف تام

7. متوافق مع المستقبل


يعد استخدام "Future" من مكتبة Scala القياسية حقيقة واقعة لعدد كبير من قواعد الكود. تأتي ZIO بأسلوب "fromFuture" ، والذي يوفر سياق تنفيذ جاهزًا:

 ZIO.fromFuture(implicit ec => // Create some Future using `ec`: ??? ) 

عند استخدام هذه الطريقة للالتفاف على Future في أحد التأثيرات ، يمكن لـ ZIO تعيين المكان الذي سيتم فيه تنفيذ Future ، وستقوم الأساليب الأخرى ، مثل evalOn ، بنقل Future بشكل صحيح إلى سياق التنفيذ المطلوب. يقبل Cats IO "Future" ، والذي تم إنشاؤه باستخدام "ExecutionContext" خارجي. هذا يعني أن Cats IO لا يمكنها تحريك تنفيذ Future وفقًا لمتطلبات evalOn أو أساليب التحول. علاوة على ذلك ، فإن هذا يثقل كاهل المستخدم بتحديد سياق التنفيذ لـ Future ، مما يعني اختيارًا ضيقًا وبيئة منفصلة.

نظرًا لأنه يمكن تجاهل ExecutionContext المقدمة ، يمكن تمثيل ZIO كمجموع من ميزات Cats IO ، مما يضمن تفاعلًا أكثر سلاسة ودقة مع Future في الحالة العامة ، ولكن لا تزال هناك استثناءات.

8. حظر IO


كما هو مبين في مقال " تجمع الخيط " . أفضل الممارسات مع ZIO "، لتطبيقات الخوادم ، يلزم وجود تجمعين منفصلين على الأقل لتحقيق أقصى قدر من الكفاءة:

  • تجمع ثابت لآثار وحدة المعالجة المركزية / غير متزامن.
  • ديناميكية ، مع إمكانية زيادة عدد حظر المواضيع.

سيؤدي قرار تشغيل جميع التأثيرات على تجمع مؤشرات الترابط الثابت إلى توقف تام في يوم ما ، بينما يؤدي تشغيل جميع التأثيرات على تجمع ديناميكي إلى فقدان الأداء.

على JVM ، توفر ZIO عمليتين تدعمان تأثيرات الحظر:

  • عامل التشغيل "Blocking (effect") ، الذي يقوم بتبديل تنفيذ تأثير معين في مجموعة مؤشرات الترابط المحظورة التي لها إعدادات مسبقة جيدة يمكن تغييرها إذا رغبت في ذلك ؛
  • «effectBlocking(effect)» , , .

, , , «blocking». , - , , «effectBlocking» , ZIO ( ).

Cats IO , . , «blocking», «evalOn», , , .

( ZIO) (, ), .

9.


, Scala, :

  • «ReaderT»/ «Kleisli», ;
  • «EitherT», ( «OptionT», «EitherT» «Unit» ).

, (, http4s «Kleisli» «OptionT»). («effect totation»), ZIO «reader» «typed error» ZIO. «reader» «typed error» , ZIO , . , «Task[A]», «reader» «typed errors».

ZIO () - . , ZIO , .

Cats IO . , , «reader» «typed errors» «state», «writer» , .

ZIO 8 Cats IO . , Scala .

10.


ZIO , , . , Scala, .

ZIO 2000 , «typed errors» , — 375 . Scala , . , , .

:

  • ;
  • ;
  • , ;
  • .

. , - , .

- . , . ZIO . Cats IO , , ZIO ( , ).

11.


ZIO , , - .

  • , : «ZIO. succeed» «Applicative[F].pure», «zip» «Apply[F].product», «ZIO.foreach» «Traverse[F].traverse».
  • (Cats, Cats Effect, Scalaz ).
  • , ( «Runtime», Cats Effect - Cats Effect). — Cats IO.
  • .
  • . : "zip"/"zipPar", "ZIO.foreach"/"ZIO.foreachPar", "ZIO.succeed"/"ZIO.succeedLazy«.
  • , «». ZIO IDE.
  • Scala ZIO : «ZIO.fromFuture», «ZIO.fromOption», «ZIO.fromEither», «ZIO.fromTry».
  • «».

, Scala, , ZIO , , , ZIO, . Cats IO , Cats.

, , , ( , , ).

12.


ZIO — - , .

:

  • , «Ref», «Promise», «Queue», «Semaphore» «Stream» //;
  • STM, , , ;
  • «Schedule», ;
  • «Clock», «Random», «Console» «System» , ;
  • , .

- Cats IO . Cats IO , ( ) .

استنتاج


Cats Effect Scala-, , .

, Cats Effect, , Cats Effect : Cats IO, Monix, Zio.

, . , , , : ZIO Cats Effect .

Scala — . , Scala. ScalaConf , 18 , John A De Goes .

Source: https://habr.com/ru/post/ar472386/


All Articles