Selviytymisopas kumpaankin monadiin Scalassa

Aloin työskennellä Scalan kanssa muutama kuukausi sitten. Yksi käsitteistä, joita minulla oli eniten vaikeuksia ymmärtää, on Eithermonadi. Joten päätin leikkiä sen kanssa ja ymmärtää paremmin sen voiman.

Tässä tarinassa jaan oppimani toivoen auttavan koodereita lähestymään tätä kaunista kieltä.

Kumpi tahansa monadi

Eitheron yksi Scalan hyödyllisimmistä monadeista. Jos mietit mitä monadi on, niin… En voi mennä yksityiskohtiin täällä, ehkä tulevassa tarinassa!

Kuvittele Eitherkuin laatikko, joka sisältää laskennan. Työskentelet tämän laatikon sisällä, kunnes päätät saada siitä lopputuloksen.

Tässä erityisessä tapauksessa Eitherlaatikollamme voi olla kaksi "lomaketta". Se voi olla (joko) a Lefttai a Right, riippuen sen sisällä olevan laskennan tuloksesta.

Kuulen sinun kysyvän: "OK, ja mihin se on hyödyllistä?"

Tavallinen vastaus on: virheiden käsittely.

Voimme laittaa laskennan Eitherja tehdä siitä a Leftvirheiden tapauksessa tai Rightsisällyttää tuloksen onnistumisen tapauksessa. Käyttö Leftvirheiden ja Rightmenestystä on sopimusta. Ymmärretään tämä koodilla!

Tässä katkelmassa määritämme vain Eithermuuttujan.

Voimme määritellä sen Rightsisältävän kelvollisen arvon tai Leftsisältävän virheen. Meillä on myös laskelma, joka palauttaa arvon Either, eli se voi olla a Lefttai a Right. Yksinkertainen, eikö olekin?

Oikea ja vasen projektio

Kun laskenta on laatikossa, saatamme haluta saada siitä arvon. Olen varma, että odottaa kutsua .geton Eitherja poimia tuloksen.

Se ei ole niin yksinkertaista.

Ajattele sitä: laitat laskutoimituksesi Either, mutta et tiedä johtuiko se a- Lefttai a- arvoon Right. Joten mitä .getpuhelun pitäisi palata? Virhe vai arvo?

Siksi tuloksen saamiseksi sinun on tehtävä oletus laskennan tuloksesta.

Tässä projektio tulee esiin.

Alkaen an Either, voit saada a RightProjectiontai a LeftProjection. Ensimmäinen tarkoittaa sitä, että oletat, että laskennan tulos on a Right, jälkimmäisen a Left.

Tiedän, tiedän ... tämä voi olla hieman hämmentävää. On parempi ymmärtää se jollakin koodilla. Loppujen lopuksi koodi kertoo aina totuuden .

Se siitä. Huomaa, että kun yrität saada tuloksen a: sta RightProjection, mutta se on a Left, saat poikkeuksen. Sama koskee a LeftProjectionja sinulla on Right.

Hienoa on, että voit kartoittaa ennusteita. Tämä tarkoittaa, että voit sanoa: "olettaa, että se on oikeus: tee se sen kanssa", jättäen Leftmuuttumattomaksi (ja päinvastoin).

Vaihtoehdosta kumpaankin

Option on toinen yleinen tapa käsitellä virheellisiä arvoja.

An Optionvoi olla arvo tai olla tyhjä (sen arvo on Nothing). Lyön vetoa, että huomasit yhtäläisyyden Either… Se on vieläkin parempi, koska voimme todella muuttaa Optionan Either! Koodiaika!

On mahdollista muuttaa Optiona: ksi a Lefttai a Right. Tuloksena oleva Eithertestamentin puoli sisältää arvon, Optionjos se on määritelty. Viileä. Odota hetki ... Entä jos Optiontyhjä on? Saamme toisen puolen, mutta meidän on määriteltävä, mitä odotamme löytävän siitä.

sisältä ulos

Eitheron taika, olemme kaikki samaa mieltä. Joten päätämme käyttää sitä epävarmoissa laskelmissamme. Tyypillinen skenaario toiminnallista ohjelmointia suoritettaessa on funktion kartoittaminen osalle Listelementeistä tai a Map. Tehdään se uudella uudella Eithervoimanlähteellä ...

Huston, meillä on "ongelma" (ok, se ei ole SUURI ongelma, mutta se on hieman epämiellyttävä). Olisi parempi, että kokoelma sisällä Eitherkuin paljon Eithersisällä kokoelma. Voimme työskennellä siinä.

Lista

Aloitetaan List. Ensin pohdimme asiaa, sitten voimme pelata koodilla.

Meidän on purettava arvo Either, laitettava se Listja laitettava luettelo Either. Hyvä, pidän siitä.

Asia on, että meillä voi olla a Lefttai a Right, joten meidän on käsiteltävä molempia tapauksia. Kunnes löydämme a Right, voimme laittaa sen arvon uuteen List. Etenemme tällä tavalla keräämällä kaikki arvot uuteen List.

Lopulta pääsemme loppuun Liston Either, eli meillä on uusi Listsisältää kaikki arvot. Voimme pakata sen Rightja olemme valmiit. Tämä oli tapaus, jossa laskelmamme ei palauttanut Errorsisäpuolta a Left.

Jos näin tapahtuu, se tarkoittaa, että jokin meni pieleen laskennassamme, joten voimme palauttaa sen Leftkanssa Error. Meillä on logiikka, nyt tarvitsemme koodin.

Kartta

Työ Mapon melko yksinkertaista, kun olemme tehneet kotitehtävät List(huolimatta siitä, että meidän on tehtävä siitä yleinen):

  • Vaihe yksi: muuttaa Map, joka Liston Eithersisältää monikon (avain, arvo).
  • Vaihe kaksi: välitä tulos funktiolle, jonka määritimme List.
  • Vaihe kolme: muuttaa Listja tuplat sisällä Eitheron Map.

Helppo nakki.

Hankitaan tyylikäs: hyödyllinen implisiittinen muunnin

Esittelimme Eitherja ymmärsimme, että siitä on hyötyä virheiden käsittelyssä. Soitimme hieman projektioilla. Näimme kuinka siirtyä A: sta Optiontoiseen Either. Olemme myös toteuttaneet joitakin hyödyllisiä toimintoja ”ote” Eitheralkaen Listja Map. Toistaiseksi niin hyvä.

Haluaisin lopettaa matkan Eithermonadissa menemällä hieman pidemmälle. Määrittelemämme apuohjelmatoiminnot tekevät tehtävänsä, mutta minusta tuntuu siltä, ​​että jotain puuttuu ...

Olisi hämmästyttävää tehdä muunnos suoraan kokoelmasta. Meillä olisi jotain myList.toEitherListtai myMap.toEitherMap. Enemmän tai vähemmän kuin mitä teemme Option.toRighttai Option.toLeft.

Hyvä uutinen: voimme tehdä sen käyttämällä implisiittisiä luokkia !

Epäsuorien luokkien käyttäminen Scalassa antaa meille mahdollisuuden laajentaa toisen luokan ominaisuuksia.

Meidän tapauksessamme laajennamme sen kykyä Listja Map"purkaa" sen automaattisesti Either. Muunnoksen toteutus on sama kuin aiemmin määritimme. Ainoa ero on, että nyt teemme siitä yleisen. Eikö Scala ole mahtava?

Koska tämä voi olla hyödyllinen hyödyllisyysluokka, valmistelin sinulle pääsisällön, jonka voit kopioida ja liittää helposti.

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

Johtopäätös

Siinä kaikki ihmiset. Toivon, että tämä novelli voi auttaa sinua ymmärtämään paremmin Eithermonadia.

Huomaa, että toteutukseni on melko yksinkertainen. Lyön vetoa, että on olemassa monimutkaisempia ja tyylikkäimpiä tapoja tehdä sama asia. Olen aloittelija Scalassa ja tykkään KISS: stä, joten mieluummin luettavuus kuin (tyylikäs) monimutkaisuus.

Jos sinulla on parempi ratkaisu, etenkin apuluokalle, olen iloinen nähdessäni sen ja oppien jotain uutta! :-)