Miksi Go on huono älykkäille ohjelmoijille

Artikkeli on kirjoitettu vastauksena aiemmin julkaistuun antipodean artikkeli.

Miksi Go on huono älykkäille ohjelmoijille

Yli kahden viime vuoden aikana olen käyttänyt Goa erikoistuneen RADIUS-palvelimen toteuttamiseen kehitetyllä laskutusjärjestelmällä. Matkan varrella opin itse kielen hienouksia. Itse ohjelmat ovat hyvin yksinkertaisia ​​eivätkä ole tämän artikkelin tarkoitus, mutta itse Go-käyttökokemus ansaitsee muutaman sanan puolustuksekseen. Gosta on tulossa yhä yleisempi kieli vakavalle, skaalautuvalle koodille. Kielen on luonut Google, jossa sitä käytetään aktiivisesti. Bottom line, uskon rehellisesti, että Go-kielen suunnittelu on huono UN-älykkäille ohjelmoijille.

Suunniteltu heikoille ohjelmoijille?

Heikot puhuvat ongelmista. Vahvaa puhetta ideoista ja unelmista...

Go on erittäin helppo oppia, niin helppoa, että voit lukea koodin käytännössä ilman koulutusta. Tätä kielen ominaisuutta käytetään monissa globaaleissa yrityksissä, kun koodia luetaan yhdessä ei-ydinasiantuntijoiden (esimiehet, asiakkaat jne.) kanssa. Tämä on erittäin kätevää sellaisille menetelmille kuin suunnittelulähtöinen kehitys.
Jopa aloittelevat ohjelmoijat alkavat tuottaa melko kunnollista koodia viikon tai kahden kuluttua. Kirja, josta opiskelin, on "Go Programming" (kirjoittaja Mark Summerfield). Kirja on erittäin hyvä, se koskettaa monia kielen vivahteita. Tarpeettoman monimutkaisten kielten, kuten Java, PHP, jälkeen taikuuden puute on virkistävää. Mutta ennemmin tai myöhemmin monet rajoitetut ohjelmoijat saavat ajatuksen käyttää vanhoja menetelmiä uudella alalla. Onko tämä todella tarpeellista?

Rob Pike (kielen pääideologi) loi Go-kielen teolliseksi kieleksi, jota on helppo ymmärtää ja tehokas käyttää. Kieli on suunniteltu maksimaaliseen tuottavuuteen suurissa tiimeissä, eikä siitä ole epäilystäkään. Monet aloittelevat ohjelmoijat valittavat, että heiltä puuttuu monia ominaisuuksia. Tämä yksinkertaisuuden halu oli kielen suunnittelijoiden tietoinen päätös, ja ymmärtääksemme täysin, miksi sitä tarvitaan, meidän on ymmärrettävä kehittäjien motivaatio ja mitä he yrittivät saavuttaa Golla.

Joten miksi se tehtiin niin yksinkertaiseksi? Tässä pari lainausta Rob Pikesta:

Tärkeintä tässä on, että ohjelmoijamme eivät ole tutkijoita. He ovat pääsääntöisesti melko nuoria, tulevat meille opiskelun jälkeen, ehkä he ovat opiskelleet Javaa tai C/C++:ta tai Pythonia. He eivät ymmärrä hienoa kieltä, mutta samalla haluamme heidän luovan hyviä ohjelmistoja. Siksi kielen tulee olla helposti ymmärrettävää ja opittavaa.

Hänen pitäisi olla tuttu, karkeasti sanottuna samanlainen kuin C. Googlella työskentelevät ohjelmoijat aloittavat uransa varhain ja tuntevat enimmäkseen proseduurikielet, erityisesti C-perheen. Uuden ohjelmointikielen nopean tuottavuuden vaatimus tarkoittaa, että kieli ei saa olla liian radikaalia.

Viisaita sanoja, eikö?

Yksinkertaisuuden esineitä

Yksinkertaisuus on kauneuden välttämätön edellytys. Lev Tolstoi.

Yksinkertaisuuden pitäminen on yksi tärkeimmistä tavoitteista kaikissa suunnittelussa. Kuten tiedät, täydellinen projekti ei ole projekti, johon ei ole mitään lisättävää, vaan sellainen, josta ei ole mitään poistettavaa. Monet ihmiset uskovat, että monimutkaisten ongelmien ratkaisemiseksi (tai jopa ilmaisemiseksi) tarvitaan monimutkainen työkalu. Se ei kuitenkaan ole. Otetaan esimerkiksi PERL-kieli. Kieliideologit uskoivat, että ohjelmoijalla tulisi olla vähintään kolme erilaista tapaa ratkaista yksi ongelma. Go-kielen ideologit valitsivat toisen tien, he päättivät, että yksi tapa, mutta todella hyvä, riitti tavoitteen saavuttamiseen. Tällä lähestymistavalla on vakava perusta: ainoa tapa on helpompi oppia ja vaikeampi unohtaa.

Monet siirtolaiset valittavat, että kieli ei sisällä elegantteja abstraktioita. Kyllä, tämä on totta, mutta tämä on yksi kielen tärkeimmistä eduista. Kieli sisältää vähintään taikuutta - joten ohjelman lukeminen ei vaadi syvällistä tietoa. Mitä tulee koodin monimuotoisuuteen, tämä ei ole ongelma ollenkaan. Hyvin kirjoitettu Golang-ohjelma lukee pystysuunnassa, vähän tai ei ollenkaan rakennetta. Lisäksi ohjelman lukunopeus on vähintään suuruusluokkaa suurempi kuin sen kirjoitusnopeus. Jos ajattelet, että koko koodilla on yhtenäinen muotoilu (tehty sisäänrakennetulla gofmt-komennolla), muutaman ylimääräisen rivin lukeminen ei ole ongelma ollenkaan.

Ei kovin ilmeikäs

Taide ei siedä, kun sen vapautta rajoitetaan. Tarkkuus ei ole hänen vastuullaan.

Yksinkertaisuuden halusta johtuen Go:lta puuttuu konstruktioita, joita niihin tottuneet ihmiset pitävät muilla kielillä luonnollisina. Aluksi se voi olla hieman epämukavaa, mutta sitten huomaat, että ohjelma on paljon helpompi ja yksiselitteisempi lukea.

Esimerkiksi konsoliapuohjelma, joka lukee stdinin tai tiedoston komentoriviargumenteista, näyttää tältä:

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {

    flag.Parse()

    scanner := newScanner(flag.Args())

    var text string
    for scanner.Scan() {
        text += scanner.Text()
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Println(text)
}

func newScanner(flags []string) *bufio.Scanner {
    if len(flags) == 0 {
        return bufio.NewScanner(os.Stdin)
    }

    file, err := os.Open(flags[0])

    if err != nil {
        log.Fatal(err)
    }

    return bufio.NewScanner(file)
}

Ratkaisu samaan ongelmaan D:ssä, vaikka se näyttää hieman lyhyemmältä, ei ole helpompi lukea

import std.stdio, std.array, std.conv;

void main(string[] args)
{
    try
    {
        auto source = args.length > 1 ? File(args[1], "r") : stdin;
        auto text   = source.byLine.join.to!(string);

        writeln(text);
    }
    catch (Exception ex)
    {
        writeln(ex.msg);
    }
}

Helvettiä kopiointia

Ihminen kantaa helvettiä sisällään. Martti Luther.

Aloittelijat valittavat jatkuvasti Gosta geneeristen lääkkeiden puutteen vuoksi. Tämän ongelman ratkaisemiseksi useimmat käyttävät suoraa koodin kopiointia. Esimerkiksi funktio kokonaislukulistan summaamiseksi, tällaiset ammattilaiset uskovat, että toiminnallisuutta ei voida toteuttaa millään muulla tavalla kuin yksinkertaisella kopiointi-liittämisellä kullekin tietotyypille.

package main

import "fmt"

func int64Sum(list []int64) (uint64) {
    var result int64 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func int32Sum(list []int32) (uint64) {
    var result int32 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func main() {

    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(int32Sum(list32))
    fmt.Println(int64Sum(list64))
}

Kielellä on riittävät keinot toteuttaa tällaisia ​​rakenteita. Esimerkiksi yleinen ohjelmointi olisi hyvä.

package main

import "fmt"

func Eval32(list []int32, fn func(a, b int32)int32) int32 {
    var res int32
    for _, val := range list {
        res = fn(res, val)
    }
    return res
}

func int32Add(a, b int32) int32 {
    return a + b
}

func int32Sub(a, b int32) int32 {
    return a + b
}

func Eval64(list []int64, fn func(a, b int64)int64) int64 {
    var res int64
    for _, val := range list {
        res = fn(res, val)
    }
    return res
}

func int64Add(a, b int64) int64 {
    return a + b
}

func int64Sub(a, b int64) int64 {
    return a - b
}

func main() {

    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(Eval32(list32, int32Add))
    fmt.Println(Eval64(list64, int64Add))
    fmt.Println(Eval64(list64, int64Sub))
}

Ja vaikka koodimme osoittautui hieman pidemmäksi kuin edellinen tapaus, se on yleistynyt. Siksi meidän ei tule olemaan vaikeaa toteuttaa kaikkia aritmeettisia operaatioita.

Monet sanovat, että D-ohjelma näyttää huomattavasti lyhyemmältä, ja he ovat oikeassa.

import std.stdio;
import std.algorithm;

void main(string[] args)
{
    [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln;
}

Se on kuitenkin vain lyhyempi, mutta ei oikeampi, koska D-toteutus jättää täysin huomioimatta virheenkäsittelyongelman.

Todellisessa elämässä, kun logiikan monimutkaisuus kasvaa, kuilu kapenee nopeasti. Kuilu sulkeutuu vielä nopeammin, kun joudut suorittamaan toiminnon, jota ei voida suorittaa tavallisilla kielioperaattoreilla.

Ylläpidettävyyden, laajennettavuuden ja luettavuuden suhteen mielestäni Go-kieli voittaa, vaikka se häviääkin sanallisuudessa.

Yleinen ohjelmointi antaa joissain tapauksissa meille kiistattomia etuja. Tämä näkyy selvästi lajittelupaketissa. Joten minkä tahansa luettelon lajittelemiseksi meidän on vain otettava käyttöön sort.Interface-liitäntä.

import "sort"

type Names []string

func (ns Names) Len() int {
    return len(ns)
}

func (ns Names) Less(i, j int) bool {
    return ns[i] < ns[j]
}

func (ns Names) Swap(i, j int) {
    ns[i], ns[j] = ns[j], ns[i]
}

func main() {
    names := Names{"London", "Berlin", "Rim"}
    sort.Sort(names)
}

Jos otat minkä tahansa avoimen lähdekoodin projektin ja suoritat grep "interface{}" -R -komennon, näet kuinka usein hämmentäviä käyttöliittymiä käytetään. Läheiset toverit sanovat heti, että kaikki tämä johtuu geneeristen lääkkeiden puutteesta. Näin ei kuitenkaan aina ole. Otetaan esimerkkinä DELPHI. Huolimatta samojen yleisten ominaisuuksien olemassaolosta, se sisältää erityisen VARIANT-tyypin operaatioita varten mielivaltaisilla tietotyypeillä. Go-kieli tekee samoin.

Tykistä varpusiin

Ja pakkopaidan täytyy sopia hulluuden kokoon. Stanislav Lec.

Monet extreme-fanit saattavat väittää, että Golla on toinen mekanismi geneeristen lääkkeiden luomiseen - heijastus. Ja he ovat oikeassa... mutta vain harvoissa tapauksissa.

Rob Pike varoittaa meitä:

Tämä on tehokas työkalu, jota tulee käyttää varoen. Sitä tulee välttää, ellei se ole ehdottoman välttämätöntä.

Wikipedia kertoo meille seuraavaa:

Reflektiolla tarkoitetaan prosessia, jonka aikana ohjelma voi seurata ja muokata omaa rakennettaan ja käyttäytymistään suorituksen aikana. Reflektorin taustalla olevaa ohjelmointiparadigmaa kutsutaan reflektiiviseksi ohjelmoimiseksi. Tämä on eräänlainen metaohjelmointi.

Kuitenkin, kuten tiedät, sinun on maksettava kaikesta. Tässä tapauksessa se on:

  • vaikeuksia kirjoittaa ohjelmia
  • ohjelman suoritusnopeus

Siksi heijastusta on käytettävä varoen, kuten suuren kaliiperin asetta. Ajattelematon heijastuksen käyttö johtaa lukukelvottomiin ohjelmiin, jatkuviin virheisiin ja hitaisiin nopeuksiin. Snob-ohjelmoijalle on juuri sopivaa esitellä koodiaan muiden, pragmaattisempien ja vaatimattomien kollegoiden edessä.

Xin kulttuurimatkatavarat? Ei, useilta kieliltä!

Omaisuuden ohella perillisille jätetään myös velkoja.

Huolimatta siitä, että monet uskovat kielen perustuvan kokonaan C-perintöön, näin ei ole. Kieli sisältää monia parhaiden ohjelmointikielten näkökohtia.

syntaksi

Ensinnäkin kielioppirakenteiden syntaksi perustuu C-kielen syntaksiin. DELPHI-kielellä oli kuitenkin myös merkittävä vaikutus. Näin ollen näemme, että ylimääräiset sulut, jotka heikentävät huomattavasti ohjelman luettavuutta, on poistettu kokonaan. Kieli sisältää myös DELPHI-kielelle ominaisen ":="-operaattorin. Pakettien käsite on lainattu kielistä, kuten ADA. Käyttämättömien entiteettien ilmoitus on lainattu PROLOG-kielestä.

Semantiikka

Paketit perustuivat DELPHI-kielen semantiikkaan. Jokainen paketti kapseloi tiedot ja koodin ja sisältää yksityisiä ja julkisia kokonaisuuksia. Tämän avulla voit vähentää paketin käyttöliittymän minimiin.

Toteutustoiminto delegointimenetelmällä on lainattu DELPHI-kielestä.

Kokoelma

Ei ilman syytä, että siellä on vitsi: Go kehitettiin C-ohjelman käännösvaiheessa. Yksi kielen vahvuuksista on sen erittäin nopea käännös. Idea lainattiin DELPHI-kielestä. Jokainen Go-paketti vastaa DELPHI-moduulia. Nämä paketit käännetään uudelleen vain silloin, kun niitä todella tarvitaan. Siksi seuraavan muokkauksen jälkeen sinun ei tarvitse kääntää koko ohjelmaa, vaan vain muuttuneet paketit ja niistä riippuvat paketit (ja silloinkin vain, jos pakettien käyttöliittymät ovat muuttuneet).

Korkeatasoiset rakenteet

Kieli sisältää monia erilaisia ​​korkean tason rakenteita, jotka eivät millään tavalla liity matalan tason kieliin, kuten C.

  • linjat
  • Hash-taulukot
  • Viipaleita
  • Ankan kirjoittaminen on lainattu kielistä, kuten RUBY (jota valitettavasti monet eivät ymmärrä tai käytä täysimääräisesti).

Muistin hallinta

Muistinhallinta ansaitsee yleensä erillisen artikkelin. Jos kielissä, kuten C++, hallinta jätetään kokonaan kehittäjälle, niin myöhemmissä kielissä, kuten DELPHI, käytettiin viitelaskentamallia. Tällä lähestymistavalla sykliset viittaukset eivät olleet sallittuja, koska muodostui orpoja klustereita, joten Gossa on sisäänrakennettu tällaisten klustereiden havaitseminen (kuten C#). Lisäksi roskakeräin on tehokkaampi kuin useimmat tällä hetkellä tunnetut toteutukset ja sitä voidaan jo käyttää moniin reaaliaikaisiin tehtäviin. Kieli itse tunnistaa tilanteet, joissa pinoon voidaan varata arvo muuttujan tallentamiseksi. Tämä vähentää muistinhallinnan kuormitusta ja lisää ohjelman nopeutta.

Samanaikaisuus ja samanaikaisuus

Kielen rinnakkaisuus ja kilpailukyky on kiitettävä. Mikään matalan tason kieli ei voi edes etäältä kilpailla Go:n kanssa. Ollakseni rehellinen, on syytä huomata, että mallia eivät keksineet kielen kirjoittajat, vaan se on yksinkertaisesti lainattu vanhasta hyvästä ADA-kielestä. Kieli pystyy käsittelemään miljoonia rinnakkaisia ​​yhteyksiä kaikilla prosessoreilla, samalla kun sillä on suuruusluokkaa vähemmän monimutkaisia ​​ongelmia lukkiutumisesta ja kilpailuolosuhteista, jotka ovat tyypillisiä monisäikeiselle koodille.

Lisäedut

Jos se on kannattavaa, kaikista tulee epäitsekkäitä.

Kieli tarjoaa meille myös monia kiistattomia etuja:

  • Yksi suoritettava tiedosto projektin rakentamisen jälkeen yksinkertaistaa sovellusten käyttöönottoa huomattavasti.
  • Staattinen kirjoittaminen ja tyyppipäätelmät voivat vähentää merkittävästi koodisi virheiden määrää, jopa ilman testien kirjoittamista. Tiedän joitain ohjelmoijia, jotka tekevät ilman testien kirjoittamista ollenkaan ja heidän koodinsa laatu ei kärsi merkittävästi.
  • Erittäin yksinkertainen ristiin käännös ja standardikirjaston erinomainen siirrettävyys, mikä yksinkertaistaa huomattavasti eri alustojen sovellusten kehittämistä.
  • RE2-säännölliset lausekkeet ovat säikeen turvallisia ja niillä on ennakoitavissa olevat suoritusajat.
  • Tehokas vakiokirjasto, jonka avulla useimmat projektit voidaan tehdä ilman kolmannen osapuolen kehyksiä.
  • Kieli on riittävän tehokas keskittyäkseen ongelmaan sen ratkaisemisen sijaan, mutta silti riittävän alhainen, jotta ongelma voidaan ratkaista tehokkaasti.
  • Go eco -järjestelmä sisältää jo valmiiksi kehitettyjä työkaluja kaikkiin tilanteisiin: testit, dokumentaatio, paketinhallinta, tehokkaat linterit, koodin luominen, kilpailuolosuhteiden ilmaisin jne.
  • Go-versio 1.11 esitteli sisäänrakennetun semanttisen riippuvuuden hallinnan, joka on rakennettu suositun VCS-isännöinnin päälle. Kaikki Go-ekosysteemin muodostavat työkalut käyttävät näitä palveluita koodin lataamiseen, rakentamiseen ja asentamiseen yhdellä iskulla. Ja se on hienoa. Version 1.11 saapumisen myötä myös paketin versiointiongelma ratkesi täysin.
  • Koska kielen ydinajatus on vähentää taikuutta, kieli kannustaa kehittäjiä käsittelemään virheitä selkeästi. Ja tämä on oikein, koska muuten se yksinkertaisesti unohtaa virheiden käsittelyn kokonaan. Toinen asia on, että useimmat kehittäjät jättävät tietoisesti huomioimatta virheiden käsittelyn, vaan haluavat käsitellä virheen yksinkertaisesti eteenpäin eteenpäin.
  • Kieli ei toteuta klassista OOP-metodologiaa, koska sen puhtaassa muodossaan Go:ssa ei ole virtualiteettia. Tämä ei kuitenkaan ole ongelma rajapintoja käytettäessä. OOP:n puuttuminen vähentää merkittävästi aloittelijoiden pääsyn estettä.

Yksinkertaisuus yhteisön hyödyksi

Se on helppo monimutkaista, vaikea yksinkertaistaa.

Go on suunniteltu yksinkertaiseksi ja se onnistuu tässä tavoitteessa. Se on kirjoitettu älykkäille ohjelmoijille, jotka ymmärtävät ryhmätyön edut ja ovat kyllästyneet Enterprise-tason kielten loputtomaan vaihteluun. Sen arsenaalissa on suhteellisen pieni joukko syntaktisia rakenteita, joten se ei käytännössä muutu ajan myötä, joten kehittäjillä on paljon aikaa vapautunut kehittämiseen, ei loputtomiin kieliinnovaatioiden tutkimiseen.

Yritykset saavat myös useita etuja: alhainen tuloeste mahdollistaa nopean asiantuntijan löytämisen, ja kielen muuttumattomuus mahdollistaa saman koodin käytön jopa 10 vuoden kuluttua.

Johtopäätös

Suuri aivokoko ei ole koskaan tehnyt yhdestäkään norsusta Nobel-palkinnon voittajaa.

Niille ohjelmoijille, joiden henkilökohtainen ego menee tiimihengen edelle, sekä teoreetikoille, jotka rakastavat akateemisia haasteita ja loputonta "itsensä kehittämistä", kieli on todella huonoa, koska se on yleiskäyttöinen artesaanikieli, joka ei salli esteettistä nautintoa työsi tuloksista ja näytä itsesi ammattitaidolla kollegoiden edessä (edellyttäen, että mittaamme älykkyyttä näillä kriteereillä, emme älykkyysosamäärällä). Kuten kaikki elämässä, se on henkilökohtaisista prioriteeteista kiinni. Kuten kaikki arvokkaat innovaatiot, kieli on jo kulkenut pitkän matkan yleisestä kieltämisestä joukkohyväksyntään. Kieli on nerokas yksinkertaisuudessaan, ja kuten tiedät, kaikki nerokas on yksinkertaista!

Lähde: will.com

Lisää kommentti