Miks Go on ebatarkade programmeerijate jaoks halb?

Artikkel on kirjutatud vastusena varem avaldatule antipoodide artikkel.

Miks Go on ebatarkade programmeerijate jaoks halb?

Viimase kahe aasta jooksul olen kasutanud Go-d arendatud arveldussüsteemiga spetsiaalse RADIUS-serveri juurutamiseks. Teel õpin ma keele enda keerukust. Programmid ise on väga lihtsad ega ole selle artikli eesmärk, kuid Go kasutamise kogemus väärib kaitseks paar sõna. Go on muutumas tõsise, skaleeritava koodi jaoks üha populaarsemaks keeleks. Keele lõi Google, kus seda aktiivselt kasutatakse. Kokkuvõtteks arvan ausalt, et Go keele disain on UNintelligentsete programmeerijate jaoks halb.

Mõeldud nõrkadele programmeerijatele?

Nõrgad räägivad probleemidest. Tugev jutt ideedest ja unistustest...

Go on väga lihtne õppida, nii lihtne, et saate koodi lugeda praktiliselt ilma igasuguse koolituseta. Seda keele funktsiooni kasutatakse paljudes globaalsetes ettevõtetes, kui koodi loetakse koos mittepõhispetsialistidega (juhid, kliendid jne). See on väga mugav selliste metoodikate jaoks nagu disainipõhine arendus.
Isegi algajad programmeerijad hakkavad nädala või paari pärast üsna korralikku koodi tootma. Raamat, millest õppisin, on "Go Programming" (autor Mark Summerfield). Raamat on väga hea, puudutab paljusid keelenüansse. Pärast tarbetult keerulisi keeli, nagu Java, PHP, värskendab maagia puudumine. Kuid varem või hiljem tekib paljudel piiratud programmeerijatel idee kasutada vanu meetodeid uues valdkonnas. Kas see on tõesti vajalik?

Rob Pike (keele peamine ideoloog) lõi Go keele tööstuskeelena, mida on lihtne mõista ja mida on tõhus kasutada. Keel on loodud maksimaalse tootlikkuse saavutamiseks suurtes meeskondades ja selles pole kahtlust. Paljud algajad programmeerijad kurdavad, et neil on palju funktsioone, mis neil puuduvad. See lihtsuse soov oli keele disainerite teadlik otsus ja selleks, et täielikult mõista, miks seda vaja oli, peame mõistma arendajate motivatsiooni ja seda, mida nad Go-ga püüdsid saavutada.

Miks see siis nii lihtsaks tehti? Siin on paar tsitaati Rob Pike'ilt:

Põhimõte on siin see, et meie programmeerijad ei ole teadlased. Nad on reeglina üsna noored, tulevad meile peale õppimist, võib-olla õppisid Java, C/C++ või Python. Nad ei saa suurepärasest keelest aru, kuid samal ajal tahame, et nad looks head tarkvara. Seetõttu peaks keel olema kergesti mõistetav ja õpitav.

Ta peaks olema tuttav, jämedalt öeldes sarnane C-ga. Google'is töötavad programmeerijad alustavad oma karjääri varakult ja tunnevad enamasti protseduurikeeli, eriti C-perekonda. Uue programmeerimiskeele kiire tootlikkuse nõue tähendab, et keel ei tohiks olla liiga radikaalne.

Targad sõnad, kas pole?

Lihtsuse artefaktid

Lihtsus on ilu vajalik tingimus. Lev Tolstoi.

Lihtsus on iga disaini üks olulisemaid eesmärke. Nagu teate, pole täiuslik projekt mitte projekt, kuhu pole midagi lisada, vaid see, millelt pole midagi eemaldada. Paljud inimesed usuvad, et keerukate probleemide lahendamiseks (või isegi väljendamiseks) on vaja keerulist tööriista. Siiski ei ole. Võtame näiteks PERL-i keele. Keeleideoloogid arvasid, et programmeerijal peaks ühe probleemi lahendamiseks olema vähemalt kolm erinevat viisi. Go keele ideoloogid läksid teist teed, nad otsustasid, et eesmärgi saavutamiseks piisab ühest, kuid tõeliselt heast teest. Sellel lähenemisel on tõsine alus: ainus viis on lihtsam õppida ja raskem unustada.

Paljud rändajad kurdavad, et keel ei sisalda elegantseid abstraktsioone. Jah, see on tõsi, kuid see on keele üks peamisi eeliseid. Keel sisaldab minimaalselt maagiat – seega pole programmi lugemiseks vaja sügavaid teadmisi. Mis puudutab koodi paljusõnalisust, siis see pole üldse probleem. Hästi kirjutatud Golangi programm loeb vertikaalselt, vähese struktuuriga või üldse mitte. Lisaks on programmi lugemise kiirus vähemalt suurusjärgu võrra suurem kui selle kirjutamise kiirus. Kui arvate, et kogu kood on ühtse vorminguga (see on tehtud sisseehitatud käsuga gofmt), siis pole mõne lisarea lugemine üldse probleem.

Mitte väga väljendusrikas

Kunst ei talu, kui tema vabadust piiratakse. Täpsus ei ole tema kohustus.

Lihtsuseiha tõttu puuduvad Go-l konstruktsioonid, mida teistes keeltes harjunud inimesed tajuvad millegi loomulikuna. Alguses võib see olla mõnevõrra ebamugav, kuid siis märkad, et programmi on palju lihtsam ja ühemõttelisem lugeda.

Näiteks konsooliutiliit, mis loeb stdini või faili käsurea argumentidest, näeb välja selline:

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

Sama ülesande lahendus D-s, kuigi see tundub mõnevõrra lühem, pole seda lihtsam lugeda

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

Kuradi kopeerimine

Inimene kannab endas põrgut. Martin Luther.

Algajad kurdavad Go kohta pidevalt geneeriliste ravimite puudumise pärast. Selle probleemi lahendamiseks kasutab enamik neist otsest koodi kopeerimist. Näiteks täisarvude loendi summeerimise funktsioon, usuvad sellised potentsiaalsed spetsialistid, et seda funktsiooni ei saa rakendada muul viisil kui iga andmetüübi jaoks lihtsalt kopeerides-kleepides.

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

Keelel on piisavalt vahendeid selliste konstruktsioonide elluviimiseks. Näiteks sobiks üldine programmeerimine.

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 kuigi meie kood osutus eelmisest juhtumist mõnevõrra pikemaks, on see muutunud üldistatuks. Seetõttu ei ole meil keeruline kõiki aritmeetilisi tehteid rakendada.

Paljud ütlevad, et D-s olev programm näeb oluliselt lühem välja ja neil on õigus.

import std.stdio;
import std.algorithm;

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

Kuid see on ainult lühem, kuid mitte õigem, kuna D-rakendus ignoreerib täielikult vigade käsitlemise probleemi.

Reaalses elus väheneb loogika keerukuse kasvades vahe kiiresti. Vahe kaob veelgi kiiremini, kui peate sooritama toimingu, mida ei saa teha standardsete keeleoperaatoritega.

Hooldatavuse, laiendatavuse ja loetavuse osas võidab minu arvates Go keel, kuigi paljusõnalisuses kaotab.

Üldine programmeerimine annab meile mõnel juhul vaieldamatut kasu. Seda illustreerib selgelt sorteerimispakett. Niisiis, mis tahes loendi sortimiseks peame lihtsalt rakendama liidese sort.Interface.

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

Kui võtate mõne avatud lähtekoodiga projekti ja käivitate käsu grep "liides{}" -R, näete, kui sageli segaseid liideseid kasutatakse. Lähedased seltsimehed ütlevad kohe, et see kõik on tingitud geneeriliste ravimite puudumisest. See ei ole aga alati nii. Võtame näiteks DELPHI. Vaatamata samade üldnimetuste olemasolule sisaldab see suvaliste andmetüüpidega toimingute jaoks spetsiaalset tüüpi VARIANT. Go keel teeb sama.

Suurtükist varblasteni

Ja sundsärk peab vastama hulluse suurusele. Stanislav Lec.

Paljud äärmuslikud fännid võivad väita, et Go’l on geneeriliste ravimite loomiseks veel üks mehhanism – peegeldus. Ja neil on õigus... aga ainult harvadel juhtudel.

Rob Pike hoiatab meid:

See on võimas tööriist, mida tuleks kasutada ettevaatusega. Seda tuleks vältida, kui see pole tingimata vajalik.

Wikipedia ütleb meile järgmist:

Peegeldus viitab protsessile, mille käigus programm saab täitmise ajal jälgida ja muuta oma struktuuri ja käitumist. Refleksiooni aluseks olevat programmeerimisparadigmat nimetatakse peegeldavaks programmeerimiseks. See on teatud tüüpi metaprogrammeerimine.

Kuid nagu teate, peate kõige eest maksma. Sel juhul on see:

  • raskused programmide kirjutamisel
  • programmi täitmise kiirus

Seetõttu tuleb peegeldust kasutada ettevaatusega, nagu suurekaliibrilist relva. Peegelduse mõtlematu kasutamine toob kaasa loetamatud programmid, pidevad vead ja madala kiiruse. Just see, et snoobist programmeerija saaks oma koodiga teiste, pragmaatilisemate ja tagasihoidlikumate kolleegide ees eputada.

Xi kultuuripagas? Ei, mitmest keelest!

Koos varandusega jäävad pärijatele ka võlad.

Hoolimata asjaolust, et paljud usuvad, et keel põhineb täielikult C-pärandil, pole see nii. Keel hõlmab paljusid parimate programmeerimiskeelte aspekte.

süntaks

Esiteks põhineb grammatiliste struktuuride süntaks C-keele süntaksil. Kuid ka DELPHI keelel oli märkimisväärne mõju. Seega näeme, et üleliigsed sulud, mis oluliselt vähendavad programmi loetavust, on täielikult eemaldatud. Keel sisaldab ka DELPHI keelele omast operaatorit ":=". Pakettide kontseptsioon on laenatud sellistest keeltest nagu ADA. Kasutamata olemite deklaratsioon on laenatud PROLOGi keelest.

Semantika

Paketid põhinesid DELPHI keele semantikal. Iga pakett sisaldab andmeid ja koodi ning sisaldab era- ja avalikke üksusi. See võimaldab vähendada paketi liidest miinimumini.

Rakendusoperatsioon delegeerimismeetodi abil laenati DELPHI keelest.

Koostamine

Pole asjata, et seal on nali: Go töötati välja C-programmi koostamise ajal. Üks keele tugevusi on selle ülikiire koostamine. Idee laenati DELPHI keelest. Iga Go pakett vastab DELPHI moodulile. Need paketid kompileeritakse uuesti ainult siis, kui see on tõesti vajalik. Seetõttu ei pea pärast järgmist redigeerimist kogu programmi kompileerima, vaid ümber kompileerima ainult muudetud paketid ja nendest muudetud pakettidest sõltuvad paketid (ja ka siis ainult juhul, kui paketiliidesed on muutunud).

Kõrgetasemelised konstruktsioonid

Keel sisaldab palju erinevaid kõrgetasemelisi konstruktsioone, mis ei ole kuidagi seotud madala taseme keeltega nagu C.

  • Stringid
  • Räsi tabelid
  • Viilud
  • Pardi tippimine on laenatud sellistest keeltest nagu RUBY (mida kahjuks paljud ei mõista ega kasuta täiel määral ära).

Mälu haldamine

Mäluhaldus väärib üldiselt eraldi artiklit. Kui sellistes keeltes nagu C++ jäetakse juhtimine täielikult arendaja hooleks, siis hilisemates keeltes nagu DELPHI kasutati viiteloendusmudelit. Selle lähenemisviisi korral ei olnud tsüklilised viited lubatud, kuna moodustusid orbklastrid, siis on Go-l selliste klastrite tuvastamine (nt C#) sisseehitatud. Lisaks on prügikoguja tõhusam kui enamik praegu teadaolevaid rakendusi ja seda saab juba kasutada paljude reaalajas ülesannete jaoks. Keel ise tunneb ära olukorrad, kui pinusse saab määrata muutuja salvestamise väärtuse. See vähendab mäluhalduri koormust ja suurendab programmi kiirust.

Samaaegsus ja samaaegsus

Keele paralleelsus ja konkurentsivõime on väljaspool kiitust. Ükski madalatasemeline keel ei suuda Go-ga isegi eemalt konkureerida. Ausalt öeldes tasub märkida, et mudelit ei leiutanud keele autorid, vaid see oli lihtsalt laenatud vanast heast ADA keelest. Keel on võimeline töötlema miljoneid paralleelühendusi, kasutades kõiki CPU-sid, samas kui sellel on suurusjärgu võrra vähem keerukaid probleeme ummikseisude ja võistlustingimustega, mis on tüüpilised mitmelõimelise koodi jaoks.

Lisahüved

Kui see on tulus, muutuvad kõik ennastsalgavaks.

Keel pakub meile ka mitmeid kahtlemata eeliseid:

  • Üks käivitatav fail pärast projekti loomist lihtsustab rakenduste juurutamist oluliselt.
  • Staatiline tippimine ja tüübi järeldamine võivad märkimisväärselt vähendada teie koodis esinevate vigade arvu isegi ilma teste kirjutamata. Tean mõnda programmeerijat, kes teeb üldse ilma testide kirjutamiseta ja nende koodi kvaliteet oluliselt ei kannata.
  • Väga lihtne ristkompileerimine ja standardteegi suurepärane kaasaskantavus, mis lihtsustab oluliselt platvormidevaheliste rakenduste arendamist.
  • RE2 regulaaravaldised on lõimekindlad ja nende täitmisajad on prognoositavad.
  • Võimas standardteek, mis võimaldab enamikul projektidel hakkama ilma kolmanda osapoole raamistiketa.
  • Keel on piisavalt võimas, et keskenduda probleemile, mitte sellele, kuidas seda lahendada, kuid piisavalt madal, et probleemi saaks tõhusalt lahendada.
  • Go eco süsteem sisaldab juba karbist välja töötatud tööriistu kõikideks puhkudeks: testid, dokumentatsioon, pakendihaldus, võimsad linterid, koodi genereerimine, võistlustingimuste detektor jne.
  • Go versioon 1.11 tutvustas sisseehitatud semantilise sõltuvuse haldust, mis on üles ehitatud populaarsele VCS-hostimisele. Kõik Go ökosüsteemi moodustavad tööriistad kasutavad neid teenuseid, et neist ühe hoobiga koodi alla laadida, luua ja installida. Ja see on suurepärane. Versiooni 1.11 saabumisega lahenes täielikult ka probleem paketiversiooniga.
  • Kuna keele põhiidee on maagia vähendamine, motiveerib see keel arendajaid selgesõnaliselt vigade käsitlemiseks. Ja see on õige, sest vastasel juhul unustab see vigade käsitlemise täielikult. Teine asi on see, et enamik arendajaid ignoreerib teadlikult veakäsitlust, eelistades nende töötlemise asemel vea lihtsalt ülespoole edastada.
  • Keel ei rakenda klassikalist OOP-metoodikat, kuna puhtal kujul Go-s ei ole virtuaalsust. Liideste kasutamisel pole see aga probleem. OOP puudumine vähendab oluliselt algajate sisenemisbarjääri.

Lihtsus kogukonna hüvanguks

Seda on lihtne keeruliseks teha, raske lihtsustada.

Go loodi lihtsaks ja see saavutab selle eesmärgi. See on kirjutatud nutikatele programmeerijatele, kes mõistavad meeskonnatöö eeliseid ja on väsinud Enterprise-taseme keelte lõputust varieeruvusest. Kuna selle arsenalis on suhteliselt väike kogum süntaktilisi struktuure, ei muutu see aja jooksul praktiliselt muutumatuks, nii et arendajatel on palju aega arendamiseks, mitte keeleuuenduste lõputuks uurimiseks.

Ettevõtted saavad ka mitmeid eeliseid: madal sisenemisbarjäär võimaldab kiiresti leida spetsialisti ning keele muutumatus võimaldab kasutada sama koodi ka 10 aasta pärast.

Järeldus

Suur aju suurus pole kunagi teinud ühestki elevandist Nobeli preemia laureaadiks.

Nende programmeerijate jaoks, kelle isiklik ego on meeskonnavaimu ees ülimuslik, samuti teoreetikute jaoks, kes armastavad akadeemilisi väljakutseid ja lõputut "enesetäiendamist", on keel tõesti halb, kuna see on üldotstarbeline käsitöökeel, mis ei võimalda teil esteetilist naudingut oma töö tulemusest ja näidake end kolleegide ees professionaalselt (eeldusel, et mõõdame intelligentsust nende kriteeriumide, mitte IQ järgi). Nagu kõik elus, on see isiklike prioriteetide küsimus. Nagu kõik väärt uuendused, on ka keel juba kaugele jõudnud universaalsest eitusest massilise aktsepteerimiseni. Keel on oma lihtsuses geniaalne ja nagu teate, kõik geniaalne on lihtne!

Allikas: www.habr.com

Lisa kommentaar