Toiminnallinen ohjelmointi Android-kehittäjille - Osa 1

Viime aikoina olen viettänyt paljon aikaa oppimalla Elixiriä, joka on mahtava toiminnallinen ohjelmointikieli, joka on ystävällinen aloittelijoille.

Tämä sai minut ajattelemaan: miksi et käyttäisi joitain toimintamaailman käsitteitä ja tekniikoita Android-ohjelmoinnissa?

Kun useimmat ihmiset kuulevat termin Toiminnallinen ohjelmointi, he ajattelevat Hacker News -viestejä, jotka ymmerivät monadeista, korkeamman tilauksen toiminnoista ja abstrakteista tietotyypeistä. Se näyttää olevan mystinen maailmankaikkeus, joka on kaukana päivittäisen ohjelmoijan vaivoista, varattu vain mahtavimmille hakkereille, jotka polveutuvat Númenorin alueelta.

No, ruuvaa se ! Olen täällä kertomassa sinulle, että sinäkin voit oppia sen. Sinäkin voit käyttää sitä. Sinäkin voit luoda kauniita sovelluksia sen avulla. Sovellukset, joissa on tyylikäs, luettava koodipohja ja vähemmän virheitä.

Tervetuloa Functional Programming (FP) -sovellukseen Android-kehittäjille. Tässä sarjassa opimme FP: n perusteet ja kuinka voimme käyttää niitä vanhassa hyvässä Javassa ja uudessa mahtavassa Kotlinissa. Ajatuksena on pitää käsitteet käytännöllisyydessä ja välttää mahdollisimman paljon akateemista ammattikieltä.

FP on valtava aihe. Opimme vain käsitteet ja tekniikat, jotka ovat hyödyllisiä Android-koodin kirjoittamiseen. Saatamme käydä muutamassa konseptissa, joita emme voi suoraan käyttää täydellisyyden vuoksi, mutta yritän pitää materiaalin mahdollisimman asiaankuuluvana.

Valmis? Mennään.

Mikä on toiminnallinen ohjelmointi ja miksi minun pitäisi käyttää sitä?

Hyvä kysymys. Termi Funktionaalinen ohjelmointi on sateenvarjo useille ohjelmointikonsepteille, joita monikerta ei oikein tee. Sen ytimessä se on ohjelmointityyli, joka käsittelee ohjelmia matemaattisten toimintojen arviointina ja välttää muuttuvat tilat ja sivuvaikutukset (puhumme näistä riittävän pian).

Ytimessä FP korostaa:

  • Vakuutuskoodi - Ohjelmoijien tulisi huolehtia siitä, mitä ja antaa kääntäjän ja ajonajan huolehtia siitä, miten .
  • Selkeä todistus - Koodin on oltava mahdollisimman ilmeinen. Erityisesti sivuvaikutukseton eristettävä yllätysten välttämiseksi. Tietovirta ja virheenkäsittely on määritelty yksiselitteisesti, ja konstruktioita, kuten GOTO-käskyjä ja poikkeuksia, vältetään, koska ne voivat laittaa sovelluksesi odottamattomiin tiloihin.
  • Samanaikaisuus - Useimmat toiminnalliset koodit ovat oletusarvoisesti samanaikaisia toiminnallisen puhtauden käsitteen takia . Yleinen yhteisymmärrys näyttää olevan, että erityisesti tämä piirre aiheuttaa toiminnallisen ohjelmoinnin kasvavan suosiota, koska CPU-ytimet eivät ole nopeammat joka vuosi kuten ennen (katso Mooren lakia), ja meidän on tehtävä ohjelmistamme samanaikaisempia hyödyntämään moniydinarkkitehtuureista.
  • Korkeamman asteen funktiot - Funktiot ovat ensiluokkaisia ​​jäseniä, kuten kaikki muutkin kielen primitiivit. Voit siirtää funktioita ympäri samalla tavalla kuin merkkijono tai int.
  • Muuttamattomuus - Muuttujia ei saa muuttaa, kun ne on alustettu. Kun jokin asia on luotu, se on se ikuisesti. Jos haluat sen muuttuvan, luot uuden asian. Tämä on toinen osa selventämistä ja sivuvaikutusten välttämistä. Jos tiedät, että asia ei voi muuttua, sinulla on paljon enemmän luottamusta sen tilaan, kun käytät sitä.

Deklaratiivinen, eksplisiittinen ja samanaikainen koodi, josta on helpompi päättää ja joka on suunniteltu välttämään yllätyksiä? Toivon, että olen herättänyt kiinnostuksenne.

Aloitetaan tässä sarjan ensimmäisessä osassa FP: n keskeisimpiä käsitteitä: puhtaus , sivuvaikutukset ja tilaaminen .

Puhtaat toiminnot

Funktio on puhdas, jos sen lähtö riippuu vain sen syötteestä ja sillä ei ole sivuvaikutuksia (puhumme sivuvaikutebitistä heti tämän jälkeen). Katsotaanpa esimerkki, eikö niin?

Harkitse tätä yksinkertaista toimintoa, joka lisää kaksi numeroa. Se lukee yhden numeron tiedostosta ja toinen numero välitetään parametrina.

Java

int add(int x) { int y = readNumFromFile(); return x + y;}

Kotlin

fun add(x: Int): Int { val y: Int = readNumFromFile() return x + y}

Tämän toiminnon lähtö ei ole riippuvainen yksinomaan sen tulosta. Riippuen siitä, mitä readNumFromFile () palauttaa, sillä voi olla eri lähdöt samalla arvolla x . Tämän toiminnon sanotaan olevan epäpuhdas .

Muunna se puhtaaksi funktioksi.

Java

int add(int x, int y) { return x + y;}

Kotlin

fun add(x: Int, y: Int): Int { return x + y}

Nyt toiminnon lähtö riippuu vain sen tuloista. Annetuille x: lle ja y: lle funktio palauttaa aina saman tuloksen. Tämän toiminnon sanotaan nyt olevan puhdas . Myös matemaattiset toiminnot toimivat samalla tavalla. Matemaattisten funktioiden tuotos riippuu vain sen panoksista - Siksi toiminnallinen ohjelmointi on paljon lähempänä matematiikkaa kuin tavallinen ohjelmointityyli, johon olemme tottuneet.

PS Tyhjä tulo on edelleen tulo. Jos funktio ei ota syötteitä ja palauttaa saman vakion joka kerta, se on silti puhdas.

PPS Ominaisuus palauttaa aina sama lähtö tietylle tulolle tunnetaan myös viitteellisenä läpinäkyvyytenä, ja saatat nähdä sen käytetyn puhuttaessa puhtaista toiminnoista.

Sivuvaikutukset

Tutkitaan tätä käsitettä samalla lisäystoiminnon esimerkillä. Muutamme lisäystoimintoa kirjoittamaan tulos myös tiedostoon.

Java

int add(int x, int y) { int result = x + y; writeResultToFile(result); return result;}

Kotlin

fun add(x: Int, y: Int): Int { val result = x + y writeResultToFile(result) return result}

Tämä toiminto kirjoittaa nyt laskennan tuloksen tiedostoon. eli se muuttaa nyt ulkomaailman tilaa. Tällä toiminnolla sanotaan nyt olevan sivuvaikutus, eikä se ole enää puhdas toiminto.

Koodilla, joka muuttaa ulkomaailman tilaa - muuttaa muuttujaa, kirjoittaa tiedostoon, kirjoittaa DB: lle, poistaa jotain jne., Sanotaan olevan sivuvaikutus.

Toimintoja, joilla on sivuvaikutuksia, vältetään FP: ssä, koska ne eivät ole enää puhtaita ja riippuvat historiallisesta kontekstista . Koodin konteksti ei ole itsenäinen. Tämä tekee heistä paljon vaikeampi perustella.

Oletetaan, että kirjoitat koodinpätkää, joka riippuu välimuistista. Nyt koodisi tulos riippuu siitä, kirjoittaako joku välimuistiin, mitä siihen kirjoitettiin, milloin se kirjoitettiin, jos tiedot ovat kelvollisia jne. Et voi ymmärtää, mitä ohjelmasi tekee, ellet ymmärrä kaikkia mahdollisia tiloja välimuistista se riippuu. Jos laajennat tämän koskemaan kaikkia muita asioita, joista sovelluksesi riippuu - verkko, tietokanta, tiedostot, käyttäjän syötteet ja niin edelleen, on erittäin vaikea tietää, mitä tarkalleen tapahtuu, ja sovittaa kaikki yhteen päähän kerralla.

Tarkoittaako tämä sitä, ettemme silloin käytä verkkoa, tietokantoja ja välimuisteja? Ei tietenkään. Suorituksen lopussa haluat sovelluksen tekevän jotain. Android-sovellusten tapauksessa se tarkoittaa yleensä käyttöliittymän päivittämistä, jotta käyttäjä voi todella saada jotain hyödyllistä sovelluksestamme.

FP: n suurin idea ei ole luopua sivuvaikutuksista kokonaan, vaan hillitä ja eristää ne. Sen sijaan, että sovelluksemme olisi täynnä toimintoja, joilla on sivuvaikutuksia, työnnämme sivuvaikutuksia järjestelmämme reunoille, jotta niillä olisi mahdollisimman vähän vaikutuksia, mikä tekee sovelluksestamme helpomman perustellun. Puhumme tästä yksityiskohtaisesti, kun tutkimme sovellusten toiminnallista arkkitehtuuria myöhemmin sarjassa.

Tilaaminen

Jos meillä on joukko puhtaita toimintoja, joilla ei ole sivuvaikutuksia, niiden suorittamisjärjestys muuttuu merkityksettömäksi.

Oletetaan, että meillä on toiminto, joka kutsuu 3 puhdasta toimintoa sisäisesti:

Java

void doThings() { doThing1(); doThing2(); doThing3();}

Kotlin

fun doThings() { doThing1() doThing2() doThing3()}

Tiedämme varmasti, että nämä toiminnot eivät ole riippuvaisia ​​toisistaan ​​(koska yhden tulos ei ole toisen tulo), ja tiedämme myös, että ne eivät muuta mitään järjestelmässä (koska ne ovat puhtaita). Tämä tekee niiden toteuttamisjärjestyksen täysin vaihdettavissa.

Suoritusjärjestys voidaan sekoittaa uudelleen ja optimoida itsenäisille puhtaille toiminnoille. Huomaa, että jos panos doThing2 () olivat seurausta doThing1 () niin nämä olisi suoritettu järjestyksessä, mutta doThing3 () voisi silti olla uudelleen määrättiin suorittamaan ennen doThing1 ().

Mitä tämä tilaava ominaisuus kuitenkin saa meidät? Samanaikaisuus, se mitä! Voimme suorittaa nämä toiminnot kolmella erillisellä CPU-ytimellä huolimatta siitä, että ruuvaat mitään.

Monissa tapauksissa kääntäjät edistyneillä puhtailla toiminnallisilla kielillä, kuten Haskell, voivat kertoa analysoimalla koodisi muodollisesti, onko se samanaikainen vai ei, ja voivat estää sinua ampumasta itseäsi umpikujaan, kilpailuolosuhteisiin ja vastaaviin. Nämä kääntäjät voivat teoriassa myös rinnastaa koodisi automaattisesti (tätä ei todellakaan ole missään kääntäjässä, jonka tunnen tällä hetkellä, mutta tutkimus on käynnissä).

Vaikka kääntäjäsi ei katsekaan näitä juttuja, ohjelmoijana on hienoa pystyä kertomaan, onko koodisi samanaikainen, vain katsomalla funktion allekirjoituksia ja välttämään ilkeitä ketjutusvirheitä, jotka yrittävät rinnastaa pakollista koodia, joka saattaa olla täynnä piilotettuja sivuvaikutukset.

Yhteenveto

Toivon, että tämä ensimmäinen osa on kiehtonut sinua FP: stä. Puhtaat, sivuvaikutuksettomat toiminnot helpottavat koodin päättämistä ja ovat ensimmäinen askel samanaikaisuuden saavuttamiseksi.

Ennen kuin pääsemme samanaikaisuuteen, meidän on kuitenkin opittava muuttumattomuudesta . Teemme juuri tämän sarjan osassa 2 ja näemme, kuinka puhtaat toiminnot ja muuttumattomuus voivat auttaa meitä kirjoittamaan yksinkertaisen ja helposti ymmärrettävän samanaikaisen koodin turvautumatta lukkoihin ja mutekkeihin.

Lue seuraava

Toiminnallinen ohjelmointi Android-kehittäjille - Osa 2

Jos et ole lukenut osaa 1, lue se täältä: medium.com

Jos pidit tästä, napsauta? alla. Huomaan jokaisen ja olen kiitollinen heistä kaikista.

Jos haluat lisätietoja ohjelmoinnista, seuraa minua, niin saat ilmoituksen, kun kirjoitan uusia viestejä.