Túlélési útmutató a Scala egyik monádjához

Néhány hónapja kezdtem dolgozni a Scalával. Az egyik olyan fogalom, amelynek megértése a legnehezebb volt, a Eithermonád. Tehát úgy döntöttem, hogy játszom vele, és jobban megértem az erejét.

Ebben a történetben megosztom a tanultakat, remélve, hogy segítek a kódolóknak megközelíteni ezt a gyönyörű nyelvet.

Bármelyik monád

Eithera Scala egyik leghasznosabb monádja. Ha kíváncsi vagy arra, mi a monád, nos ... itt nem részletezhetem a részleteket, talán egy későbbi történetben!

Képzelje el, Eithermint egy számítást tartalmazó dobozt. Addig dolgozol ebben a mezőben, amíg el nem dönted az eredmény elérését.

Ebben a konkrét esetben Eitherdobozunknak két „formája” lehet. Ez lehet (akár) a Leftvagy a Right, a benne lévő számítás eredményétől függően.

Hallom, amikor azt kérdezi: "OK, és mire hasznos?"

A szokásos válasz: hibakezelés.

Helyezhetünk egy számítást a hibába, Eitherés hibává tehetjük azt Left, vagy Rightsiker esetén eredményt tartalmazhatunk. A Lefthibák és Righta siker használata konvenció. Értsük meg ezt valamilyen kóddal!

Ebben a részletben csak egy Eitherváltozót határozunk meg .

Meghatározhatjuk Rightérvényes értéket Lefttartalmazóként vagy hibát tartalmazóként. Van olyan számításunk is, amely egy an-t ad vissza Either, vagyis lehet a Leftvagy a Right. Egyszerű, nem igaz?

Jobb és bal vetület

Miután a számítás a mezőbe kerül, érdemes kihozni belőle az értéket. Biztos vagyok benne, számíthat arra, hogy hívja a .getszóló Eitherés kivonat az eredmény.

Ez nem olyan egyszerű.

Gondoljon csak bele: beletette a számítását a Either, de nem tudja, hogy a Leftvagy a eredményt adott- e Right. Tehát mit kell .getvisszatérnie egy hívásnak? A hiba, vagy az érték?

Ezért az eredmény eléréséhez feltételeznie kell a számítás eredményét.

Itt jön létre a vetítés .

An-tól kezdve Eitherkaphat egy RightProjectionvagy egy LeftProjection. Az előbbi azt jelenti, hogy feltételezed, hogy a számítás a Right, az utóbbi a Left.

Tudom, tudom ... ez kissé zavaró lehet. Jobb valamilyen kóddal megérteni. Végül is a kód mindig igazat mond .

Ez az. Ne feledje, hogy amikor megpróbálja megszerezni az eredményt a-ból RightProjection, de ez egy Left, akkor kivételt kap. Ugyanez vonatkozik a LeftProjectionés van egy Right.

A klassz dolog az, hogy leképezheti a vetületeket. Ez azt jelenti, hogy azt mondhatja: „tegyük fel, hogy ez Jobb: tedd meg vele”, Leftváltozatlanul hagyva (és fordítva).

Az Opciótól bármelyikig

Option az érvénytelen értékek kezelésének másik gyakori módja.

Egy Optionértéknek lehet értéke, vagy üres is lehet (értéke van Nothing). Fogadok, hogy észrevett egy hasonlóságot Either… Még jobb, mert valójában átalakíthatunk egy Optionan-t Either! Kódidő!

Átalakítható az Optiona-ra Leftvagy a-ra Right. A Eithervégrendelet eredő oldala tartalmazza az értéket, Optionha meg van adva. Menő. Várjon egy percet ... Mi van, ha az Optionüres? Megkapjuk a másik oldalt, de meg kell határoznunk, hogy mit várunk benne találni.

Kifordítva

Eithervarázslat, ebben mindannyian egyetértünk. Tehát úgy döntünk, hogy bizonytalan számításokhoz használjuk. A funkcionális programozás tipikus forgatókönyve a függvény leképezése egy Listelemre vagy egy elemre Map. Csináljuk a friss, új teljesítményű Eitherszámításunkkal ...

Huston, van egy "problémánk" (ok, ez nem NAGY probléma, de kissé kényelmetlen). Jobb lenne, ha a gyűjtemény belül lenne, Eithermint a sok Eitherbenne. Dolgozhatunk ezen.

Lista

Kezdjük azzal List. Először okoskodunk erről, aztán játszhatunk kóddal.

Ki kell szednünk az értéket a -ból Either, be kell tennünk a-ba List, és beletesszük a listát egy Either. Jó, ez tetszik.

A lényeg az, hogy lehet egy Leftvagy egy Right, tehát mindkét esetet kezelnünk kell. Amíg nem találunk a-t Right, addig egy újba tudjuk tenni az értékét List. Így haladunk, és minden értéket felhalmozunk az újban List.

Végül elérjük a végén a Listaz Either, ami azt jelenti, hogy van egy új List, amely az összes értéket. Csomagolhatjuk a-ba Rightés kész. Ez volt az eset, amikor a számításunk nem adott vissza egy Errora Left.

Ha ez megtörténik, ez azt jelenti, hogy valami rosszul történt a számítás során, így visszaadhatjuk Lefta Error. Megvan a logika, most szükségünk van a kódra.

Térkép

A munka Mapmeglehetősen egyszerű, miután elkészítettük a házi feladatot List(annak ellenére, hogy általánosvá kellett tenni):

  • Első lépés: átalakítani az Mapegy Listaz Either, amely tartalmazza a tuple (kulcs, érték).
  • Második lépés: adja át az eredményt az általunk definiált függvénynek List.
  • Harmadik lépés: átalakítja a Lista sorok belsejében Eitheregy Map.

Könnyű Peasy.

Nézzük klassz: hasznos implicit átalakító

Bevezettük Eitherés megértettük, hogy hasznos a hibakezelésben. Vetítésekkel játszottunk egy kicsit. Láttuk, hogyan lehet átadni egy Optionolyan Either. Azt is végrehajtott néhány hasznos funkciók „kivonat” Either-tól Listés Map. Eddig jó.

Szeretném befejezni utunkat a Eithermonádban, egy kicsit tovább haladva. Az általunk meghatározott segédfunkciók elvégzik a dolgukat, de úgy érzem, valami hiányzik ...

Csodálatos lenne, ha a konverziót közvetlenül a gyűjteményen végeznénk. Nálunk lenne valami myList.toEitherListvagy myMap.toEitherMap. Többé-kevésbé tetszik, amit csinálunk a Option.toRightvagy Option.toLeft.

Jó hír: implicit osztályok segítségével megtehetjük !

A Scala implicit osztályainak használata kibővíti egy másik osztály képességeit.

In our case, we extend the capability of List and Map to automagically “extract” the Either. The implementation of the conversion is the same we defined before. The only difference is that now we make it generic. Isn’t Scala awesome?

Since this can be a useful utility class, I prepared for you a gist you can copy and paste with ease.

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) } }

Conclusion

That’s all folks. I hope this short story may help you to better understand the Either monad.

Please note that my implementation is quite simple. I bet there are more complex and elegant ways to do the same thing. I’m a newbie in Scala and I like to KISS, so I prefer readability over (elegant) complexity.

If you have a better solution, especially for the utility class, I will be happy to see it and learn something new! :-)