Iteratee

I funktionell programmering är en iteratee en komponerbar abstraktion för inkrementell bearbetning av sekventiellt presenterade bitar av indata på ett rent funktionellt sätt. Med iterater är det möjligt att lätt omvandla hur en resurs kommer att avge data, till exempel genom att konvertera varje del av indata till versaler när de hämtas eller genom att begränsa data till endast de fem första bitarna utan att ladda hela indata i minne. Iteratees är också ansvariga för att öppna och stänga resurser, vilket ger förutsägbar resurshantering.

I varje steg presenteras en iterate med en av tre möjliga typer av värden: nästa databit, ett värde för att indikera att ingen data är tillgänglig eller ett värde för att indikera att iterationsprocessen har avslutats. Den kan returnera en av tre möjliga typer av värden, för att indikera för den som ringer vad som ska göras härnäst: en som betyder "stopp" (och innehåller det slutliga returvärdet), en som betyder "fortsätt" (och anger hur man fortsätter) , och en som betyder "signalera ett fel". De senare typerna av värden representerar i själva verket de möjliga "tillstånden" för en iterat. En iteratee skulle vanligtvis börja i "fortsätt"-tillståndet.

Iteratees används i Haskell och Scala (i Play Framework och i Scalaz), och är även tillgängliga för F# . Det finns olika lite olika implementeringar av iterater. Till exempel, i Play-ramverket involverar de Futures så att asynkron bearbetning kan utföras.

Eftersom iterater anropas av annan kod som matar dem med data, är de ett exempel på inversion av kontroll . Men till skillnad från många andra exempel på invertering av kontroll som SAX XML-parsning, behåller den iterate en begränsad kontroll över processen. Den kan inte backa tillbaka och titta på tidigare data (såvida den inte lagrar den datan internt), men den kan stoppa processen rent utan att göra ett undantag (att använda undantag som ett sätt att styra flödet , snarare än att signalera en exceptionell händelse, är ofta rynkade på av programmerare).

Vanligtvis associerade abstraktioner

Följande abstraktioner är strikt sett inte nödvändiga för att arbeta med iterater, men de gör det mer bekvämt.

Uppräknare

En Enumerator är en bekväm abstraktion för att mata in data till en iterate från en godtycklig datakälla. Vanligtvis tar uppräkningsledaren hand om all nödvändig resursrensning associerad med datakällan. Eftersom enumeratorn vet exakt när den iterate har läst klart data, kommer den att göra resursrensningen (som att stänga en fil) vid exakt rätt tidpunkt – varken för tidigt eller för sent. Den kan dock göra detta utan att behöva känna till, eller vara samlokaliserad till, implementeringen av iteraten – så uppräknare och iterater utgör ett exempel på separation av bekymmer .

Uppräknade

En enumeratee är en bekväm abstraktion för att transformera utdata från antingen en uppräknare eller iteratee, och mata den utdata till en iteratee. Till exempel skulle en "map"-uppräkning mappa en funktion över varje inmatningsdel.

Motivationer

Iteratees skapades på grund av problem med befintliga rent funktionella lösningar på problemet med att göra input/output komponerbara men ändå korrekta. Lazy I/O i Haskell tillät rena funktioner att fungera på data på disk som om det fanns i minnet, utan att explicit göra I/O alls efter att ha öppnat filen - en slags minnesmappad filfunktion - men eftersom det var omöjligt i generellt (på grund av stoppproblemet ) för körtiden för att veta om filen eller annan resurs fortfarande behövdes, kunde överdrivet antal filer lämnas öppna i onödan, vilket resulterade i utmattning av filbeskrivningar operativsystemnivå . Traditionell C -stil I/O, å andra sidan, var för låg nivå och krävde att utvecklaren var bekymrad över lågnivådetaljer som den aktuella positionen i filen, vilket hindrade komponerbarheten. Iterater och uppräknare kombinerar de funktionella programmeringsfördelarna på hög nivå med lazy I/O, med möjligheten att kontrollera resurser och detaljer på låg nivå där det är nödvändigt, med C-style I/O.

Exempel

Används

Iteratees används i Play-ramverket för att skicka ut data till långvariga Comet- och WebSocket- anslutningar till webbläsare .

Iteratees kan också användas för att utföra inkrementell analys (det vill säga analys som inte läser all data i minnet på en gång), till exempel av JSON .

Det är dock viktigt att notera att iterater är en mycket allmän abstraktion och kan användas för godtyckliga typer av sekventiell informationsbehandling (eller blandad sekventiell/random-access-behandling) - och behöver inte involvera någon I/O alls. Detta gör det enkelt att återanvända en iterate att arbeta på en datauppsättning i minnet istället för att data flödar in från nätverket.

Historia

På sätt och vis var en avlägsen föregångare till idén om en uppräknare som skjuter in data i en kedja av en eller flera iterater, pipelinekonceptet i operativsystem. Men, till skillnad från en typisk pipeline, är iterater inte separata processer (och har därför inte överheaden för IPC ) - eller ens separata trådar, även om de kan utföra arbete på ett liknande sätt som en kedja av arbetartrådar som skickar meddelanden till varandra. Detta innebär att iterater är mer lätta än processer eller trådar - till skillnad från situationer med separata processer eller trådar behövs inga extra stackar.

Iteratees och enumerators uppfanns av Oleg Kiselyov för användning i Haskell. Senare introducerades de i Scalaz (i version 5.0; uppräknade var frånvarande och introducerades i Scalaz 7) och i Play Framework 2.0.

Formell semantik

Iterater har formellt modellerats som fria monader , vilket gör att ekvationslagar kan valideras och användas för att optimera program med iterater.

Alternativ

  • Iteratorer kan användas istället för iterater i Scala, men de är absolut nödvändiga , så är inte en rent funktionell lösning.
  • I Haskell har två alternativa abstraktioner som kallas Conduits and Pipes utvecklats. (Dessa rör är inte piper på operativsystemnivå, så som iteratees kräver de inte användning av systemanrop ). Speciellt ledningar är förknippade med väsentligt rikare bibliotek av primitiver och kombinatorer än itererade; Det finns ledningsadaptrar för inkrementella funktioner som att tolka HTML, XML, generaliserad tolkning, göra HTTP-förfrågningar och bearbeta svaren, vilket gör ledningar mer lämpade än iterater för industriell mjukvaruutveckling i Haskell, direkt.
  • Det finns också en abstraktion på hög nivå som heter maskiner . I Scala finns ett paket som heter FS2: Functional Streams for Scala , vars anor kan spåras tillbaka till maskiner via flera portar, byter namn och refaktorer.
  • finns paketet safe-lazy-io . Det ger en enklare lösning på några av samma problem, vilket i huvudsak innebär att vara "tillräckligt strikt" för att dra all data som krävs, eller kan behövas, genom en pipeline som tar hand om att rensa upp resurserna när de är färdiga.

Vidare läsning

externa länkar