Ner Snabbt i samband typ rabbit hole – RetailMeNot Engineering – Medium

Ner Snabbt i samband typ rabbit hole

Om du vill göra Snabba programmerare ryser, bara viska ord “i samband typer.” De är ett av de få Swift språk för att skriva funktioner som du är osannolikt att hitta i andra programmeringsspråk, så att de kan ta lite tid att vänja sig. Förra veckan försökte jag skriva en till synes enkel funktion, och det slutade med att spendera det mesta av min dag dykning ner tillhörande typ rabbit hole. Detta inlägg kommer att förklara hur jag (så småningom) skrev “enkelt” – funktion, och förhoppningsvis lär du dig om i samband typer, generiska begränsningar och “där klausuler” längs vägen!

Vad är målet?

jag ville ha en funktion som skulle:

  • Ta i två Arrayer (eller ArraySlices), basen och newElems som parametrar, och returnera en ny rad
  • produktionen matrisen bör göras genom att lägga till varje element i newElems basen, men bara om det inte redan var i basen
  • Alla input och output matris bör vara av samma typ T

Observera: i Swift, att få en subrange av en matris returnerar en ArraySlice istället av en annan grupp. ArraySlices finns utsikt till en del av den ursprungliga matrisen — de är i grunden bara två pekare i början och slutet av kedjan underavsnitt. De kan bli riktigt bra!

Tänk dig att du hade en rad med 2 miljarder element, och ville undersöka subarray element från 5 till 2 miljarder euro. Utan ArraySlices, pröva att subarray skulle kräva att fördela ytterligare 1,9 miljarder längd utbud, vilket skulle vara ett slöseri av tid och rum. Istället kan vi bara göra en ArraySlice som spår där subarray börjar och slutar, utan att allokera mer minne. Hell yeah.

Brottning med underskrifter typ

ArraySlices är bra. Det finns bara en nackdel: ArraySlice är en annan typ Array — och kom ihåg, vår funktion bör ta antingen typ. Så om du skriver en funktion så här:


räkna>

Swift kommer inte låta dig passera en ArraySlice i det. Visst, vi kan bara konvertera våra ArraySlices till Kedjor först, men det är inget kul. Istället, låt oss se om det finns några protokoll som både Utbud och ArraySlice möta.

jag hört docs för typ (sök efter “Uppfyller”) och funnit att de är båda Samlingarna! Så låt oss prova det här istället:


räkna>

Det finns två problem här. För det första är denna typ signatur kommer inte att stoppa människor som passerar i två olika typ Samlingar, som en samling av Ints och en array av Strängar. För det andra, vi har fått denna bisarra kompileringsfel:

protocol 'Samling' kan bara användas som en allmän begränsning eftersom den har Själv eller tillhörande krav

Vad betyder då detta?

Protokoll med tillhörande typer av

jag skulle verkligen rekommendera att titta på Alex Gallagher ‘ s utmärkt prata om protokoll med tillhörande typer (PATs) om du vill att riktigt förstå samband typer. I grund och botten, protokoll med tillhörande typer som klasser med generiska läkemedel. En viktig skillnad mellan Klappar och vanliga protokoll:

  • Normal protokoll kan användas , t ex återvända typ av en funktion, typ av en konstant, etc.
  • Klappar kan inte användas i alla av dessa platser. De kan bara att användas i <>s funktion/klass förklaringar till att definiera det.

namn=0446>Så om vi vill använda Samling PAT, vi har fått använda en generisk T, och sedan lägga till en begränsning som säger T måste vara en Insamling. Ungefär så här:


räkna>

jag läste denna signatur som säger

Välj någon typ T som är en Samling. Denna funktion kommer att ta in två Ts, och returnerar en annan T.

Vi är nu med hjälp av Samlingen som en begränsning för vår generiska typ T. Denna funktion faktiskt sammanställer! Hell yeah! Men detta kommer inte att låta oss mixa och matcha Matriser och ArraySlices. Varför inte? Tja, båda argumenten måste vara av samma typ T. När du ringer funktion, både Ts kan vara Array, eller både Ts kan vara ArraySlice, men kompilatorn kommer inte att tillåta en T-Matris och den andra vid T ArraySlice. Lyckligtvis, detta har en enkel fix:


räkna>

jag ignorerar tillbaka typ och kropp nu. Denna funktion gör att vi kan blanda och matcha Matriser och ArraySlices, eftersom T och U kan vara av olika typer (eller samma typ) så länge de är båda Samlingarna.

Vi har gjort framsteg, men vi är inte där ännu. Våra skriver signaturen är alltför brett — det kommer att låta oss passera i en Matris<String> Matris<Int>. Det är för bred, så vi måste begränsa det igen. Men hur?

Generic “där klausuler”

kom Ihåg hur Samlingen har några associerade typer? En av dem är Iterator, som i sin tur har en tillhörande typ som kallas Element. Vi vill att både T och U för att vara Samlingar av samma element. Så vi kan skriva något så här:


räkna>

Detta är en , och det ger oss möjlighet att ytterligare begränsa våra generika. Vi säger nu

  • Vår funktionen tar två argument, av T-och U
  • T måste vara en Insamling
  • U måste vara en Insamling
  • T och U är både Samlingar av samma element

namn=d0c3>Den sista tanken i denna lista är för komplicerat för att vara skriven i <>r. ≪>n endast används för att definiera generika, och begränsa dem till vissa protokoll. För något mer komplicerat, som relationer mellan olika generika, vi har att använda en “where”.

nu kan Vi skriva våra funktion kroppen och lägga till en return-sats:


räkna>

Vi är så nära, men detta kommer inte att sammanställa, eftersom vi endast kan kontrollera om basen , som innehåller element från withNewElems om deras delar är både Equatable. Så låt oss lägga till ytterligare en begränsning till vår where:


räkna>

Observera att Snabbt kompilator är smart nog att sluta U. Iterator.Del är också equatable (eftersom det är samma som T. Iterator.Element). Trevligt. Detta gör allt vi vill, och det tog bara fem linje-funktionen signatur 😅


räkna>

avsluta

Ge dig själv en KLAPP på ryggen (tyvärr)! Vi tämjas som förvånansvärt komplicerat odjuret. Varför var det så svårt?

Swift typ av system är mer “kraftfull” än andra äldre språk. Vi kan uttrycka krav som vi inte kunde uttrycka i Går eller Java (för exempel). I dessa språk, vi skulle behöva kompromissa och skriv en funktion som var för bred (t ex tog två Samlingar, inte Samlingar av T) eller för smal (t ex tog två Matriser, men inte en blandning av Utbud och ArraySlice). Träffa sweet spot i mitten kan ta lite arbete!

Det finns språk som Haskell vars typ system är ännu mer kraftfulla, men Haskell har funnits mycket längre. Programmerare har redan skrivit massor av guider till Haskell språket funktioner och “hur man skriver X i Haskell” inlägg. Så småningom, som Swift samhället växer, det kommer att bli lättare och lättare att lära sig om dessa nya funktioner språk.

jag hoppas Swift typ av system är en lite mindre förvirrande nu! Vänligen låt mig veta om jag har missat något. Glad kodning!

We will be happy to hear your thoughts

Leave a reply