En overlevelsesguide til Enten monaden i Scala

Jeg begyndte at arbejde med Scala for få måneder siden. Et af de begreber, som jeg havde mest vanskeligheder med at forstå, er Eithermonaden. Så jeg besluttede at lege med det og bedre forstå dets magt.

I denne historie deler jeg det, jeg har lært, i håb om at hjælpe kodere, der nærmer sig dette smukke sprog.

Enten monaden

Eitherer en af ​​de mest nyttige monader i Scala. Hvis du undrer dig over, hvad en monade er, ja ... Jeg kan ikke gå i detaljerne her, måske i en fremtidig historie!

Forestil dig Eithersom en kasse, der indeholder en beregning. Du arbejder inde i denne kasse, indtil du beslutter dig for at få resultatet ud af det.

I dette specifikke tilfælde kan vores Eitherboks have to “former”. Det kan være (enten) a Lefteller a Rightafhængigt af resultatet af beregningen inde i det.

Jeg kan høre dig spørge: “OK, og hvad er det nyttigt til?”

Det sædvanlige svar er: fejlhåndtering.

Vi kan sætte en beregning i Eitherog gøre det til Lefti tilfælde af fejl eller Rightindeholde et resultat i tilfælde af succes. Brug af Leftfor fejl og Rightsucces er en konvention. Lad os forstå dette med noget kode!

I dette uddrag definerer vi kun en Eithervariabel.

Vi kan definere det som en Rightindeholdende en gyldig værdi eller som Leftindeholdende en fejl. Vi har også en beregning, der returnerer en Either, hvilket betyder at den kan være a Lefteller a Right. Simpelt, er det ikke?

Højre og venstre fremspring

Når vi har beregningen i boksen, vil vi måske få værdien ud af den. Jeg er sikker på, at du forventer at kalde en .getEitherog udtrække dit resultat.

Det er ikke så simpelt.

Tænk over det: du lægger din beregning i Either, men du ved ikke, om det resulterede i a Lefteller a Right. Så hvad skal et .getopkald vende tilbage? Fejlen eller værdien?

Dette er grunden til, at for at få resultatet, skal du antage resultatet af beregningen.

Her er hvor projektionen kommer i spil.

Fra og med Eitherkan du få et RightProjectioneller et LeftProjection. Førstnævnte betyder, at du antager, at beregningen resulterede i a Right, sidstnævnte i a Left.

Jeg ved, jeg ved ... det kan være lidt forvirrende. Det er bedre at forstå det med en eller anden kode. Når alt kommer til alt, fortæller kode altid sandheden .

Det er det. Bemærk, at når du prøver at få resultatet fra en RightProjection, men det er en Left, får du en undtagelse. Det samme gælder for a, LeftProjectionog du har en Right.

Det seje er, at du kan kortlægge på fremskrivninger. Dette betyder, at du kan sige: "antag, at det er en ret: gør dette med det", og lad det Leftuændrede (og omvendt).

Fra mulighed til enten

Option er en anden almindelig måde at håndtere ugyldige værdier på.

En Optionkan have en værdi eller være tom (dens værdi er Nothing). Jeg vedder på, at du bemærkede en lighed med Either... Det er endnu bedre, fordi vi faktisk kan forvandle et Optiontil et Either! Kode tid!

Det er muligt at omdanne en Optiontil a Lefteller a Right. Den resulterende side af Eithervil indeholde værdien af, Optionhvis den er defineret. Fedt nok. Vent et øjeblik ... Hvad hvis den Optioner tom? Vi får den anden side, men vi skal specificere, hvad vi forventer at finde i den.

På vrangen

Eitherer magi, det er vi alle enige om. Så vi beslutter at bruge det til vores usikre beregninger. Et typisk scenario ved funktionel programmering er kortlægning af en funktion på en Listaf elementer eller på en Map. Lad os gøre det med vores nye, nye Eitherkraftige beregning ...

Huston, vi har et “problem” (ok, det er ikke et STOR problem, men det er lidt ubehageligt). Det ville være bedre at have samlingen inde Eitherend masser af Eitherinde i samlingen. Vi kan arbejde på det.

Liste

Lad os starte med List. Først tænker vi over det, så kan vi lege med kode.

Vi er nødt til at udtrække værdien fra Either, sætte den i Listog placere listen inde i en Either. Godt, jeg kan lide det.

Pointen er, at vi kan have a Lefteller a Right, så vi skal håndtere begge sager. Indtil vi finder en Right, kan vi sætte dens værdi i et nyt List. Vi fortsætter på denne måde med at samle enhver værdi i den nye List.

Til sidst vil vi nå slutningen af det Listaf Either, betyder, at vi har en ny List, der indeholder alle de værdier. Vi kan pakke det i en, Rightog vi er færdige. Dette var tilfældet, hvor vores beregning ikke returnerede en Errorindvendig a Left.

Hvis dette sker, betyder det, at noget gik galt i vores beregning, så vi kan returnere Leftmed Error. Vi har logikken, nu har vi brug for koden.

Kort

Arbejdet med Maper ret simpelt, når vi først har gjort hjemmearbejdet til List(trods behovet for at gøre det generisk):

  • Trin et: transformer Mapi a Listaf Eitherindeholdende tuplen (nøgle, værdi).
  • Trin to: send resultatet til den funktion, vi definerede videre List.
  • Trin tre: transformer Listtuplerne inden i Eitheri a Map.

Easy Peasy.

Lad os blive klassiske: en nyttig implicit konverter

We introduced Either and understood it is useful for error handling. We played a bit with projections. We saw how to pass from an Option to an Either. We also implemented some useful functions to “extract” Either from List and Map. So far so good.

I would like to conclude our journey in the Either monad going a little bit further. The utility functions we defined do their jobs, but I feel like something is missing…

It would be amazing to do our conversion directly on the collection. We would have something like myList.toEitherList or myMap.toEitherMap. More or less like what we do with Option.toRight or Option.toLeft.

Good news: we can do it using implicit classes!

Using implicit classes in Scala lets us extend the capabilities of another class.

I vores tilfælde udvider vi kapaciteten til Listog Maptil automatisk at "udtrække" Either. Implementeringen af ​​konverteringen er den samme, som vi definerede før. Den eneste forskel er, at vi nu gør det generisk. Er Scala ikke fantastisk?

Da dette kan være en nyttig hjælpeklasse, forberedte jeg dig en kerne, som du let kan kopiere og indsætte.

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

Konklusion

Det var alt folkens. Jeg håber, at denne novelle kan hjælpe dig med at forstå Eithermonaden bedre.

Bemærk, at min implementering er ret enkel. Jeg vedder på, at der er mere komplekse og elegante måder at gøre det samme på. Jeg er nybegynder i Scala og kan godt lide at KISS, så jeg foretrækker læsbarhed fremfor (elegant) kompleksitet.

Hvis du har en bedre løsning, især til forsyningsklassen, vil jeg være glad for at se den og lære noget nyt! :-)