Myau! Loyihangizda Mushuklardan foydalanishni hozirdan boshlang

Mushuklar kutubxonasiga muloyimlik bilan kirish.

Kirish

Mushuklar - bu Scalada funktsional dasturlash uchun abstraktsiya beradigan kutubxona.

Internetda Mushuklar haqida ikkita ajoyib xabarlar va kurslar mavjud (masalan, cho'chqa mushuklari va Skala mashqlari bo'yicha darslik), ammo ular amaliy tayyor emas, balki kutubxonada amalga oshiriladigan toifalar / turlarni o'rganishga moyildirlar. Mushuklarni mavjud kod bazalarida qanday ishlatish haqida misollardan foydalaning. Ushbu blog posti Mushuklar qila oladigan narsalarning sirtini zo'rg'a tirnaltiradi, ammo buning o'rniga siz o'zingizning Scala loyihangizda foydalanishingiz mumkin bo'lgan uslublar haqida qisqacha tanishtiradi. Agar siz har kuni "Kelajak" yoki "Variantlar" kabi biron bir monaddan foydalansangiz, Mushuklar sizning kodingizni o'qilishini soddalashtirishi va yaxshilashi mumkin.

Iltimos, GitHub-dagi Cats wiki-ga murojaat qiling, bu sizning loyihangizga kutubxonani qanday qo'shish haqida ko'rsatma. Biz butun postda 0.9.0 versiyasiga yopishib olamiz.

Keling, har bir paketdagi mavjud bo'lgan sintaksisga qarab, kutubxona to'plamidan foydalanaylik.

Variant uchun ham, ikkalasida ham yordamchilar

import mushuklari.syntax.option._

Ushbu paketni import qilish obj.some sintaksisini - Ba'zi (obj) ga teng bo'lgan sintaksisni olish imkonini beradi. Faqatgina haqiqiy farq shundaki, qiymat allaqachon ba'zi [T] dan [T] variantiga qadar ko'tarilgan.

Ba'zilarining (obj) o'rniga obj.some-dan foydalanish ba'zan birlik testlarining o'qilishini yaxshilashi mumkin. Masalan, agar siz BaseSpec, TestHelper yoki boshqa testlar uchun asosiy sinfingizni qo'shsangiz, u shunday nomlanadi:

keyin siz quyida ko'rsatilgan zanjirli sintaksisdan foydalanishingiz mumkin (agar test sinovlari skalamok asosida bo'lsa, Bartos Kovalikning yozuviga qarang):

Bu Future.successful (Ba'zi (foydalanuvchi)) dasturiga qaraganda ko'proq o'qiladi, ayniqsa, agar bu test to'plamida tez-tez takrorlansa. Zanjirli .some.asFuture-ni old tomoniga qo'ymasdan, kutilgan o'rash turiga emas, aslida nima qaytarilganiga e'tibor qaratish yordam beradi.

o'z navbatida, hech biri [T] Option.empty [T] uchun stsenariy emas, ammo bu hech kim emas, lekin None.typeto Variantlari [T] dan allaqachon chiqarilgan. Ba'zida ko'proq ixtisoslashtirilgan turni taqdim etish Scala kompilyatoriga None mavjud bo'lgan iboralar turini to'g'ri topishda yordam beradi.

cat.syntax.either._ faylini import qilish

obj.asRight - o'ng (obj), obj.asLeft chapga (obj). Ikkala holatda ham qaytarilgan qiymat turi o'ngdan yoki chapdan ikkalasiga ham kengaytiriladi. Ba'zi birlar bilan bo'lganidek, ushbu yordamchilar birlashma uchun qulaydir .as birliklarining sinovlarini o'qishni yaxshilash uchun:

Either.fromOption (variant: [A] varianti, agar yo'q bo'lsa: => E), o'z navbatida, Variantni har biriga o'zgartirish uchun foydali yordamchidir. Agar berilgan parametr Ba'zi (x) bo'lsa, u O'ng (x) bo'ladi. Aks holda, agar ichidagi qiymat yo'q bo'lsa, berilgan qiymatdan chapga aylanadi.

misollar to'plami va Kartezian sintaksisi

import qilinadigan mushuklar.inlar. ._

Mushuklar uchun asosiy bo'lgan (va umuman kategoriyalarga asoslangan funktsional dasturlash) bir necha turdagi sinflar mavjud, ularning eng asosiylari Functor, Appative va Monad. Ushbu blogda biz batafsil ma'lumotga ega bo'lmayapmiz (masalan, yuqorida aytib o'tilgan qo'llanmani ko'ring), lekin bilish kerak bo'lgan narsa shundaki, ko'pchilik Mushuklar sintaksisidan foydalanish uchun siz tuzilmalar uchun yashirin turdagi nusxalarini ham import qilishingiz kerak. ' bilan ishlash.

Odatda, tegishli cat.inidents paketini import qilish kifoya. Masalan, fyucherslar uchun o'zgarishlarni amalga oshirayotganda, siz cat.incepts.future._-ni import qilishingiz kerak bo'ladi. Variantlar va ro'yxatlar uchun mos keladigan to'plamlarga cat.incepts.option._ va cat.inmissions.list._ deyiladi. Ular Cats sintaksisi to'g'ri ishlashi kerak bo'lgan yashirin tipdagi misollar bilan ta'minlaydi.

Qo'shimcha eslatma sifatida, agar siz kerakli nusxalarni yoki sintaksis paketini topishda qiynalayotgan bo'lsangiz, mushuklarni tezkor ravishda hal qilish faqat cat.implicits._ ni import qilishdir. Bu afzal ko'rilgan echim emas, chunki bu kompilyatsiya vaqtini sezilarli darajada oshirishi mumkin - ayniqsa, loyiha davomida ko'plab fayllarda ishlatilsa. Odatda kompilyatordan echim yukining bir qismini olib tashlash uchun tor importni ishlatish yaxshi amaliyot hisoblanadi.

import mushuklar.syntax.cartesian._

Kartezian to'plami | @ | Sintaksis, bu bir nechta parametrlarni (masalan, fyucherslar) bir nechta parametrlarga to'g'ri keladigan funktsiyani qo'llash uchun sezgir konstruktsiyani yaratishga imkon beradi.

Aytaylik, bizda 3ta fyuchers bor, biri Int, bitta turi String, bitta foydalanuvchi turi va uchta parametrni qabul qiladigan usul - Int, String va User.

Bizning maqsadimiz - ushbu 3 fyuchers tomonidan hisoblangan qiymatlarga funktsiyani qo'llash. Kartezian sintaksisi yordamida bu juda oson va qisqa bo'ladi:

Yuqorida ta'kidlab o'tilganidek, | @ | uchun zarur bo'lgan aniq bo'lmagan nusxani (aniqrog'i, Cartesian [Future]) ta'minlash uchun to'g'ri ishlashi uchun siz cat.incepts.future._ manzilini import qilishingiz kerak.

Yuqoridagi g'oyani hatto qisqacha ham ifodalash mumkin:

Yuqoridagi ifoda natijasi Future [ProcessingResult] turiga kiradi. Agar zanjirlangan fyucherslardan birortasi muvaffaqiyatsiz bo'lsa, natijada kelajak kelajak zanjirdagi birinchi muvaffaqiyatsiz kelgusida bo'lgani kabi muvaffaqiyatsiz bo'ladi (bu muvaffaqiyatsiz tezkor xatti-harakatlar). Eng muhimi, kelajakda sodir bo'ladigan voqealardan farqli o'laroq, barcha fyucherslar parallel ravishda amalga oshiriladi:

Yuqoridagi parchada (kaput ostida flatMap va xaritadagi qo'ng'iroqlar tarjima qilinadi), stringFuture intFuture muvaffaqiyatli tugamaguncha ishlamaydi va shu tarzda userFuture faqat stringFuture tugagandan so'ng ishlaydi. Hisoblashlar bir-biridan mustaqil bo'lganligi sababli ularni | @ | bilan parallel ravishda ishlatish juda yaxshi o'rniga.

Tortishish

import mushuklari.syntax.traverse._

shpal

Agar sizda F (A) turiga oid ob'yekt mavjud bo'lsa (masalan, Kelajak kabi) va A => G [B] turidagi qiziqarli funktsiyalar mavjud bo'lsa, obj.map (kulgili) deb nomlash sizga F [G [ B]]. Hayotda ko'p uchraydigan holatlarda, masalan F va G - kelajakdir, siz Variantni olasiz (kelajak [B]), bu sizning xohlaganingiz emas.

shpal bu erda echim sifatida keladi. Agar siz obj.traverse (kulgili) kabi xaritalar o'rniga shpal deb atasangiz, G [F [A]] ga ega bo'lasiz, bu bizning holatlarimizda kelajakda [Variant [B]] bo'ladi; bu [kelajak [B]] variantiga qaraganda ancha foydali va osonroq.

Qo'shimcha eslatma sifatida, kelajakda sherik ob'ektida Future.traverse-ga bag'ishlangan maxsus usul ham mavjud, ammo Mushuklar versiyasi ancha oson o'qiladi va muayyan turdagi sinflar mavjud bo'lgan har qanday tuzilishda osongina ishlaydi.

ketma-ketlik

ketma-ketlik yanada sodda tushunchani anglatadi: F-G [A]] dan G [F [A]] gacha bo'lgan turlarni almashtirish haqida o'ylash mumkin, hatto shpal singari yopiq qiymatni ham xaritaga kiritmasdan.

obj.sequence aslida mushuklarda obj.traverse (identifikatsiya) sifatida amalga oshiriladi. Boshqa tomondan, obj.traverse (kulgili) obj.map (kulgili) .denga teng keladi.

yassi

Agar sizda F [A] ob'ekti bo'lsa va A => G [F [B]] turiga mos keladigan funktsiyalar mavjud bo'lsa, obj.map (f) qilish F [G [F [B]]] turini beradi. - siz xohlagan narsa bo'lishi dargumon.

Objektni xaritalar o'rniga siltash ozgina yordam beradi - buning o'rniga G [F [F [B]] olasiz. Odatda G - kelajak va F - bu List yoki Variantlar kabi narsalar bo'lganligi sababli, siz kelajak bilan [Variantlar [Variantlar [A]] yoki kelajakdagi [ro'yxat [ro'yxat [A]]] - tugallanishi qiyin.

Muammoni _.flatten qo'ng'iroqlari bilan xaritalash quyidagicha bo'lishi mumkin:

va oxirida siz istagan G [F [B]] turini olasiz.

Ammo, bu tekisTraverse deb nomlangan aniq yorliq mavjud:

va bu bizning muammolarimizni hal qiladi.

Monad transformatorlari

import mushuklari.data.OptionT

VariantT [F, A] misolini F [Variant [A]] bandini o'ralgan deb hisoblash mumkin, unda F yoki optsiyaning o'zida mavjud bo'lmagan joylashtirilgan turlarga xos bo'lgan bir nechta foydali usullar mavjud. Odatda, sizning F kelajakdagi bo'ladi (yoki ba'zan silliq DBIO, lekin buning uchun Functor yoki DBIO uchun Monad kabi Mushuklar tipidagi darslarni o'tkazish kerak). OptionT kabi o'ralgan materiallar odatda monad transformatorlari sifatida tanilgan.

F [Instrument [A]] nusxasi ichida saqlangan ichki qiymatni F [V varianti [B]] bilan A => B tipidagi funktsiyalar bilan taqqoslash odatiy hol bo'lib, buni juda verboz sintaksis yordamida amalga oshirish mumkin. kabi:

OptionT-dan foydalanib, buni quyidagicha soddalashtirish mumkin:

Yuqoridagi xarita OptionT [Future, String] turining qiymatini qaytaradi.

Kelajakdagi [Option [String]] qiymatini olish uchun OptionT misolida .value-ni chaqirish kifoya. Shu bilan birga parametr parametrlari / qaytish turlarida OptionT [Future, A] ga to'liq o'tish va kelajak deklaratsiyasida kelajakni [Variant [A] ni to'liq (yoki deyarli to'liq) bajarish uchun hayotiy echim hisoblanadi.

Variantlar misolini tuzishning bir necha yo'li mavjud. Quyidagi jadvaldagi metod sarlavhalari biroz soddalashtirilgan: har bir usul uchun zarur bo'lgan parametr parametrlari va tip sinflari o'tkazib yuborilgan.

Ishlab chiqarish kodida siz kelgusidagi [Variant [A]] misolini [F, A] ichiga solish uchun OptionT (...) sintaksisidan foydalanasiz. Boshqa usullar, o'z navbatida, birlik testlarida OptionT-tipli soxta qiymatlarni o'rnatish foydali bo'ladi.

Biz allaqachon OptionT's usullaridan birini - xaritani ko'rib chiqdik. Boshqa bir necha usul mavjud va ular asosan parametr sifatida qabul qilgan funktsiyalarning imzosi bilan farqlanadi. Oldingi jadvalda bo'lgani kabi, kutilgan turdagi darslar tashlab yuborildi.

Amalda, xaritadan va semiflatMap-dan ko'proq foydalanishingiz mumkin.

Har doim flatMap va xaritada bo'lgani kabi, siz nafaqat aniq, balki kaput ostida ham tushunarsiz holatlarda foydalanishingiz mumkin:

GetReserveFundsForUser tomonidan qaytarilgan OptionT [Future, Money] nusxasi, agar uchta tuzilgan usullardan biri Hech biriga mos kelmasa, Variantlar qaytarilmasa, qaytarilmaslikni qo'shadi. Aks holda, agar uchta qo'ng'iroqning natijasi Bittasini o'z ichiga olsa, yakuniy natijada Ayrimlari bo'ladi.

import mushuklari.data.EitherT

EitherT [F, A, B] bu Yadning monad transformatoridir - siz uni F [Either [A, B]] qiymatini o'rash deb o'ylashingiz mumkin.

Yuqoridagi bo'limda bo'lgani kabi, men usul sarlavhalarini soddalashtirdim, tip parametrlarini yoki ularning kontekst chegaralarini va pastki chegaralarini o'tkazib yubormayman.

Keling, EitherT misolini qanday yaratishni ko'rib chiqamiz:

Tushuntirish uchun: EitherT.fromEhere berilgan F-ni F ichiga, EitherT.right va EitherT.left qiymatlarini berilgan F ichiga to'g'ri va chapga mos ravishda joylashtiring. EitherT.pure, o'z navbatida, berilgan B qiymatini O'ngga, so'ngra F ga o'zgartiradi.

EitherT misolini yaratishning yana bir foydali usuli bu chapga va to'g'ri tomonga OptionT's usullaridan foydalanish:

toRight avval aytib o'tilgan Either.fromOption usuliga juda o'xshashdir: xuddi Optsion tomonidan parametrlardan birini yaratgan kabi, toRight ham OptionT-dan EitherT-ni yaratadi. Agar asl OptionTstores ba'zi qiymatlar bo'lsa, u O'ngga o'raladi; aks holda chap parametr sifatida berilgan qiymat chapga o'raladi.

toLeft - ba'zi qiymatlarni Chapga o'zgartiradigan va None ni berilgan o'ng qiymatni o'ng tomonga o'zgartiradigan toRight'ning hamkasbi. Bu amalda kam ishlatiladi, ammo xizmat qilishi mumkin. kodda noyoblikni tekshirishni amalga oshirish uchun. Agar qiymat topilgan bo'lsa, biz chap tomonni va tizimda hali yo'q bo'lsa, o'ng tomonga qaytamiz.

EitherT-da mavjud bo'lgan usullar biz OptionT-da ko'rgan usullarga juda o'xshash, ammo ba'zi jiddiy farqlar mavjud. Avvaliga, masalan, kelganda, siz chalkashib ketishingiz mumkin. xaritasi. OptionT holatida nima qilish kerakligi aniq edi: xarita kelajakda qo'shilgan Variantdan o'tib, keyin yopilgan Variantni o'zi xaritaga kiritishi kerak. Bu EitherT holatida biroz ravshan emas: u chap va o'ng qiymatlarni yoki faqat o'ng qiymatni xaritada ko'rsatishi kerakmi?

Javob shundaki, EitherT to'g'ri tomonli, shuning uchun oddiy xarita aslida to'g'ri qiymat bilan shug'ullanadi. Bu Scala standart kutubxonasida farqli o'laroq 2.11 gacha bo'lib, u o'z navbatida xolisdir: Einde-da xarita mavjud emas, faqat uning chap va o'ng tomonlari uchun.

Buni aytib, keling, EitherT [F, A, B] taklif qiladigan to'g'ri tarafkashlik usullarini ko'rib chiqaylik:

Yon izoh sifatida, EitherT-da chap tomon xaritasi yoki chap va o'ng qiymatlari, masalan, katlama yoki bimap kabi qiymatlarni xaritada aks ettiradigan ba'zi usullar mavjud (sizga ma'lum bir vaqtda kerak bo'lishi mumkin).

Sekin zanjirli tekshirish uchun EitherT juda foydalidir:

Yuqoridagi misolda biz elementlarga qarshi har xil tekshiruvlarni ketma-ket bajaramiz. Agar tekshiruvlardan birortasi bajarilmasa, natijada EitherT chap qiymatni o'z ichiga oladi. Aks holda, agar barcha tekshiruvlar Haqni bersa (albatta biz EitherT ga o'ralgan huquq degan ma'noni anglatadi), unda yakuniy natijada ham Huquq mavjud bo'ladi. Bu tezkor muvaffaqiyatsizlik: biz "Chap ish" ning birinchi natijasida tushunish oqimini samarali ravishda to'xtatmoqdamiz.

Agar siz xatolarni (masalan, foydalanuvchi tomonidan taqdim qilingan ma'lumotlar ma'lumotlari bilan ishlashda) to'playdigan tekshiruvni qidirsangiz, cat.data.Validated yaxshi tanlov bo'lishi mumkin.

Umumiy muammolar

Agar biron bir narsa kutilganidek tuzilmasa, avval barcha Mushuklar uchun kerak bo'lgan narsalar tegishli ekanligiga ishonch hosil qiling - faqat cat.implicits._ ni import qilib ko'ring va muammo hal bo'lmasligini ko'ring. Yuqorida ta'kidlab o'tilganidek, tor importdan foydalanish yaxshiroq, lekin agar kod tuzilmasa, ba'zan muammoni hal qilish uchun butun kutubxonani import qilish kerak bo'ladi.

Agar siz "Fyuchers" dan foydalanayotgan bo'lsangiz, unda "ExpressionContext" mazmunini berkitib qo'ying, aks holda "Mushuklar" kelajakdagi "sinflar" uchun yashirin misollarni keltirib chiqara olmaydi.

Tuzuvchi ko'pincha shpal va sekisemetemalar uchun tip parametrlarini kiritish bilan bog'liq muammolarga duch kelishi mumkin. Aniqlash qiyin bo'lgan narsa bu turlarni to'g'ridan-to'g'ri tanlash, masalan list.traverse [Future, Unit] (kulgili). Ba'zi holatlarda bu juda verbal bo'lishi mumkin va list.traverseU (kulgili) singari traversU va sequUUU tenglashtirilgan usullarni sinab ko'rish yaxshiroqdir. Tuzuvchilarga parametr parametrlarini aniqlashga yordam berish uchun ular ba'zi darajadagi hiyla-nayranglarni (mushuklar.Upply, shuning uchun U bilan) amalga oshiradilar.

Ba'zan manba skalak ostida o'tayotgan bo'lsa ham, IntelliJ ba'zan Mushuklarga yuklangan koddagi xatolar haqida xabar beradi. Bunday misollardan biri scalac ostida to'g'ri tuzilgan, ammo IntelliJ taqdimot kompilyatori ostidagi chekni kiritmaydigan cat.data.Nested klass usullaridan foydalanish. Bu Scala IDE ostida muammosiz ishlashi kerak.

Kelajakda o'rganishingiz uchun maslahat sifatida: amaliy dastur klassi funktsional dasturlashda muhim ahamiyatga ega bo'lishiga qaramay, tushunish biroz qiyin. Menimcha, bu Functor yoki Monadga qaraganda ancha sezgir, garchi u aslida meros ierarxiyasida Functor va Monad o'rtasida to'g'ri joylashgan bo'lsa. Amaliy dasturni tushunishga eng yaxshi yondashuv, avval qandaydir ekzotik ap operatsiyasining o'ziga emas, balki qanday qilib F (A [F] va F [B] ni F [(A, B)] ga o'zgartiradigan) mahsulotni ishlashini tushunishdir.