Miksi Go Design on huono älykkäille ohjelmoijille

Viime kuukausina olen käyttänyt Goa toteutuksiin. Todistus käsitteestä (noin: koodi idean toimivuuden testaamiseen) vapaa-ajallaan, osittain itse ohjelmointikielen opiskeluun. Itse ohjelmat ovat hyvin yksinkertaisia ​​eivätkä ole tämän artikkelin tarkoitus, mutta itse Go-käyttökokemus ansaitsee muutaman sanan siitä. Go lupaa olla (noin: artikkeli kirjoitettu vuonna 2015) suosittu kieli vakavalle skaalautuvalle koodille. Kielen on luonut Google, jossa sitä käytetään aktiivisesti. Bottom line, uskon rehellisesti, että Go-kielen suunnittelu on huono älykkäille ohjelmoijille.

Suunniteltu heikoille ohjelmoijille?

Go on erittäin helppo oppia, niin helppoa, että esittely kesti yhden illan, jonka jälkeen pystyin jo koodaamaan tuottavasti. Kirja, jonka opin käymään, on nimeltään Johdatus ohjelmointiin Goissa (käännös), se on saatavilla verkossa. Kirja, kuten itse Go-lähdekoodi, on helppolukuinen, sisältää hyviä koodiesimerkkejä ja sisältää noin 150 sivua, jotka voidaan lukea yhdellä kertaa. Tämä yksinkertaisuus on aluksi virkistävää, varsinkin ohjelmointimaailmassa, joka on täynnä liian monimutkaista tekniikkaa. Mutta lopulta, ennemmin tai myöhemmin herää ajatus: "Onko tämä todella niin?"

Google väittää, että Go:n yksinkertaisuus on sen myyntivaltti, ja kieli on suunniteltu maksimaaliseen tuottavuuteen suurissa ryhmissä, mutta epäilen sitä. On ominaisuuksia, jotka joko puuttuvat tai ovat liian yksityiskohtaisia. Ja kaikki kehittäjiin kohdistuvan luottamuksen puutteen vuoksi olettaen, etteivät he pysty tekemään mitään oikein. 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 Pike (noin: yksi Go-kielen luojista):

Tärkeintä tässä on, että ohjelmoijamme (noin: Googlen työntekijät) 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 heidän kielensä tulisi olla heidän ymmärrettävää ja oppimista helppoa.
 
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.

Mitä? Joten Rob Pike sanoo periaatteessa, että Googlen kehittäjät eivät ole niin hyviä, siksi he loivat kielen idiooteille (noin: mykistetty), jotta he voivat tehdä jotain. Millainen ylimielinen katse omaan kollegaasi kohtaan? Olen aina uskonut, että Googlen kehittäjät on valittu maapallon kirkkaimmista ja parhaista. Osaavatko he varmasti tehdä jotain vaikeampaa?

Liian yksinkertaisia ​​esineitä

Yksinkertaisuus on jokaisen suunnittelun arvoinen tavoite, ja yksinkertaisen tekeminen on vaikeaa. Kuitenkin, kun yritetään ratkaista (tai jopa ilmaista) monimutkaisia ​​ongelmia, joskus tarvitaan monimutkainen työkalu. Monimutkaisuus ja monimutkaisuus eivät ole ohjelmointikielen parhaita ominaisuuksia, mutta on olemassa keskitie, jossa kieli voi luoda tyylikkäitä abstraktioita, joita on helppo ymmärtää ja käyttää.

Ei kovin ilmeikäs

Koska Go on sitoutunut yksinkertaisuuteen, siitä puuttuu rakenteet, joita muissa kielissä pidetään luonnollisina. Tämä saattaa aluksi tuntua hyvältä idealta, mutta käytännössä se johtaa monisanaiseen koodiin. Syy tähän lienee ilmeinen - kehittäjien on oltava helppoja lukea muiden ihmisten koodia, mutta itse asiassa nämä yksinkertaistukset vain haittaavat luettavuutta. Gossa ei ole lyhenteitä: joko paljon tai ei mitään.

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

package main

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

func main() {

    flag.Parse()
    flags := flag.Args()

    var text string
    var scanner *bufio.Scanner
    var err error

    if len(flags) > 0 {

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

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

        scanner = bufio.NewScanner(file)

    } else {
        scanner = bufio.NewScanner(os.Stdin)
    }

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

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

    fmt.Println(text)
}

Vaikka tämä koodi pyrkii myös olemaan mahdollisimman yleinen, Go:n pakotettu monisanaisuus häiritsee, ja sen seurauksena yksinkertaisen ongelman ratkaiseminen johtaa suureen koodimäärään.

Tässä on esimerkiksi ratkaisu samaan ongelmaan D:

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

Ja kuka on nyt luettavampi? Annan ääneni D:lle. Hänen koodinsa on paljon luettavampi, koska hän kuvaa toimia selkeämmin. D käyttää paljon monimutkaisempia käsitteitä (noin: vaihtoehtoinen funktiokutsu и malleja) kuin Go-esimerkissä, mutta niiden ymmärtämisessä ei ole oikeastaan ​​mitään monimutkaista.

Helvettiä kopiointia

Suosittu ehdotus Go:n parantamiseksi on yleisyys. Tämä auttaa ainakin välttämään tarpeetonta koodin kopioimista tukemaan kaikkia tietotyyppejä. Esimerkiksi kokonaislukulistan summausfunktio voidaan toteuttaa millään muulla tavalla kuin kopioimalla liittämällä sen perusfunktio jokaiselle kokonaislukutyypille; muuta tapaa ei ole:

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 int16Sum(list []int16) (uint64) {
    var result int16 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

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

func main() {

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

    fmt.Println(int8Sum(list8))
    fmt.Println(int16Sum(list16))
    fmt.Println(int32Sum(list32))
    fmt.Println(int64Sum(list64))
}

Ja tämä esimerkki ei toimi edes allekirjoitetuille tyypeille. Tämä lähestymistapa rikkoo täysin periaatetta olla toistamatta itseäsi (DRY), yksi tunnetuimmista ja ilmeisimmista periaatteista, jonka huomiotta jättäminen on monien virheiden lähde. Miksi Go tekee tämän? Tämä on kielen kauhea puoli.

Sama esimerkki D:ssä:

import std.stdio;
import std.algorithm;

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

Yksinkertainen, tyylikäs ja suoraan asiaan. Tässä käytetty toiminto on reduce mallityypille ja predikaatille. Kyllä, tämä on jälleen monimutkaisempi kuin Go-versio, mutta ei älykkäiden ohjelmoijien vaikea ymmärtää. Mikä esimerkki on helpompi ylläpitää ja helpompi lukea?

Yksinkertainen järjestelmän ohitus

Luulen, että Go-ohjelmoijat, jotka lukevat tätä, vaahtoavat suusta ja huutavat: "Teet sen väärin!" No, on toinenkin tapa tehdä yleinen funktio ja tyypit, mutta se rikkoo tyyppijärjestelmän täysin!

Katso tämä esimerkki typerästä kielenkorjauksesta ongelman kiertämiseksi:

package main

import "fmt"
import "reflect"

func Reduce(in interface{}, memo interface{}, fn func(interface{}, interface{}) interface{}) interface{} {
    val := reflect.ValueOf(in)

    for i := 0; i < val.Len(); i++ {
        memo = fn(val.Index(i).Interface(), memo)
    }

    return memo
}

func main() {

    list := []int{1, 2, 3, 4, 5}

    result := Reduce(list, 0, func(val interface{}, memo interface{}) interface{} {
        return memo.(int) + val.(int)
    })

    fmt.Println(result)
}

Tämä toteutus Reduce lainattu artikkelista Idiomaattiset geneeriset tuotteet Gossa (noin: En löytänyt käännöstä, olen iloinen, jos autat tässä). No, jos se on idiomaattinen, en haluaisi nähdä ei-idiomaattista esimerkkiä. Käyttö interface{} - farssi, ja kielellä sitä tarvitaan vain kirjoittamisen ohittamiseksi. Tämä on tyhjä käyttöliittymä ja kaikki tyypit toteuttavat sen, mikä mahdollistaa täydellisen vapauden kaikille. Tämä ohjelmointityyli on hirvittävän ruma, eikä siinä vielä kaikki. Tällaiset akrobaattiset saavutukset vaativat ajonaikaisen heijastuksen käyttöä. Jopa Rob Pike ei pidä henkilöistä, jotka käyttävät tätä väärin, kuten hän mainitsi yhdessä raportissaan.

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

Ottaisin D-mallit tämän hölynpölyn sijaan. Miten kukaan voi sanoa noin interface{} luettavampi tai jopa kirjoitusturvallinen?

Riippuvuudenhallinnan surut

Golla on sisäänrakennettu riippuvuusjärjestelmä, joka on rakennettu suosittujen hosting-palvelujen tarjoajien päälle VCS. Gon mukana tulevat työkalut tietävät näistä palveluista ja voivat ladata, rakentaa ja asentaa niistä koodia yhdellä iskulla. Vaikka tämä on hienoa, versioinnissa on suuri virhe! Kyllä, on totta, että voit saada lähdekoodin palveluista, kuten github tai bitbucket, käyttämällä Go-työkaluja, mutta et voi määrittää versiota. Ja taas yksinkertaisuus hyödyllisyyden kustannuksella. En voi ymmärtää tällaisen päätöksen logiikkaa.

Kysyttyään tämän ongelman ratkaisusta Go-kehitystiimi loi foorumin ketju, jossa kerrottiin, kuinka he aikoivat kiertää tämän ongelman. Heidän suosituksensa oli kopioida koko arkisto projektiisi jonain päivänä ja jättää se "sellaisenaan". Mitä helvettiä he ajattelevat? Meillä on uskomattomia versionhallintajärjestelmiä, joissa on upea koodaus ja versiotuki, jotka Go-tekijät jättävät huomiotta ja kopioivat vain lähdekoodin.

Xin kulttuurimatkatavarat

Mielestäni Goa ovat kehittäneet ihmiset, jotka olivat käyttäneet C:tä koko elämänsä, ja ne, jotka eivät halunneet kokeilla jotain uutta. Kieltä voidaan kuvata C:ksi lisäpyörillä(orig.: apupyörät). Siinä ei ole uusia ideoita paitsi rinnakkaisuuden tukeminen (joka muuten on ihanaa), ja tämä on sääli. Sinulla on erinomainen rinnakkaisuus tuskin käyttökelpoisella, ontuvalla kielellä.

Toinen nariseva ongelma on, että Go on proseduurikieli (kuten C:n hiljainen kauhu). Päädyt kirjoittamaan koodia menettelytapatyyliin, joka tuntuu arkaaiselta ja vanhentuneelta. Tiedän, että olioohjattu ohjelmointi ei ole hopealuoti, mutta olisi hienoa pystyä tiivistämään yksityiskohdat tyyppeihin ja tarjoamaan kotelointia.

Yksinkertaisuus omaksi eduksesi

Go on suunniteltu yksinkertaiseksi ja se onnistuu tässä tavoitteessa. Se on kirjoitettu heikoille ohjelmoijille, käyttäen vanhaa kieltä mallina. Sen mukana tulee yksinkertaiset työkalut yksinkertaisten asioiden tekemiseen. Se on helppolukuinen ja helppokäyttöinen.

Se on äärimmäisen monisanainen, vailla vaikutusta ja huono älykkäille ohjelmoijille.

Kiitos mersinvald muokkauksia varten

Lähde: will.com

Lisää kommentti