Kodėl „Go“ kenkia neprotingiems programuotojams

Straipsnis buvo parašytas kaip atsakas į anksčiau paskelbtą antipodinis straipsnis.

Kodėl „Go“ kenkia neprotingiems programuotojams

Per pastaruosius dvejus metus naudoju „Go“, kad įdiegčiau specializuotą RADIUS serverį su išvystyta atsiskaitymo sistema. Pakeliui mokausi pačios kalbos subtilybių. Pačios programos yra labai paprastos ir nėra šio straipsnio tikslas, tačiau pati „Go“ naudojimo patirtis nusipelno kelių gynybinių žodžių. „Go“ tampa vis dažniau naudojama rimto, keičiamo kodo kalba. Kalbą sukūrė Google, kur ji aktyviai naudojama. Apatinė eilutė, aš nuoširdžiai manau, kad Go kalbos dizainas yra blogas UNintelligent programuotojams.

Skirta silpniems programuotojams?

Silpnieji kalba apie problemas. Stiprios kalbos apie idėjas ir svajones...

„Go“ labai lengva išmokti, taip lengva, kad kodą galite perskaityti praktiškai nesimokydami. Ši kalbos savybė naudojama daugelyje pasaulinių įmonių, kai kodas skaitomas kartu su nepagrindiniais specialistais (vadybininkais, klientais ir kt.). Tai labai patogu naudojant tokias metodikas kaip projektavimas.
Net pradedantieji programuotojai po savaitės ar dviejų pradeda gaminti gana padorų kodą. Knyga, iš kurios mokiausi, yra „Programavimas“ (autorius Markas Summerfieldas). Knyga labai gera, paliečiama daug kalbos niuansų. Po bereikalingai sudėtingų kalbų, tokių kaip Java, PHP, magijos trūkumas atgaivina. Tačiau anksčiau ar vėliau daugelis ribotų programuotojų turi idėją panaudoti senus metodus naujoje srityje. Ar tai tikrai būtina?

Robas Pike'as (pagrindinis kalbos ideologas) sukūrė Go kalbą kaip pramoninę kalbą, kurią lengva suprasti ir efektyviai naudoti. Kalba skirta maksimaliam produktyvumui didelėse komandose ir dėl to nekyla jokių abejonių. Daugelis pradedančiųjų programuotojų skundžiasi, kad jiems trūksta daugybės funkcijų. Šis paprastumo troškimas buvo sąmoningas kalbos kūrėjų sprendimas, o norėdami visiškai suprasti, kam to reikėjo, turime suprasti kūrėjų motyvaciją ir tai, ką jie bandė pasiekti „Go“.

Taigi kodėl tai buvo taip paprasta? Štai keletas citatų iš Robo Pike'o:

Svarbiausia yra tai, kad mūsų programuotojai nėra tyrinėtojai. Jie, kaip taisyklė, gana jauni, pas mus ateina po studijų, galbūt studijavo Java, C/C++, Python. Jie nesupranta puikios kalbos, bet tuo pat metu norime, kad jie sukurtų gerą programinę įrangą. Štai kodėl kalba turi būti lengvai suprantama ir išmokstama.

Jis turėtų būti pažįstamas, grubiai tariant, panašus į C. „Google“ dirbantys programuotojai savo karjerą pradeda anksti ir dažniausiai yra susipažinę su procedūrinėmis kalbomis, ypač C šeima. Greito produktyvumo reikalavimas naudojant naują programavimo kalbą reiškia, kad kalba neturėtų būti per daug radikali.

Išmintingi žodžiai, ar ne?

Paprastumo artefaktai

Paprastumas yra būtina grožio sąlyga. Levas Tolstojus.

Paprastumas yra vienas iš svarbiausių bet kokio dizaino tikslų. Kaip žinote, tobulas projektas yra ne projektas, kuriame nėra ką pridėti, o toks, iš kurio nėra ką pašalinti. Daugelis žmonių mano, kad norint išspręsti (ar net išreikšti) sudėtingas problemas, reikalingas sudėtingas įrankis. Tačiau taip nėra. Paimkime, pavyzdžiui, PERL kalbą. Kalbos ideologai manė, kad programuotojas turi turėti bent tris skirtingus būdus išspręsti vieną problemą. Go kalbos ideologai pasuko kitu keliu, jie nusprendė, kad tikslui pasiekti pakanka vieno, bet tikrai gero. Šis požiūris turi rimtą pagrindą: vienintelis būdas yra lengviau išmokti ir sunkiau pamiršti.

Daugelis migrantų skundžiasi, kad kalboje nėra elegantiškų abstrakcijų. Taip, tai tiesa, bet tai vienas iš pagrindinių kalbos privalumų. Kalboje yra mažiausiai magijos, todėl norint skaityti programą nereikia gilių žinių. Kalbant apie kodo išsamumą, tai nėra problema. Gerai parašyta „Golang“ programa skaitoma vertikaliai, su maža struktūra arba jos visai nėra. Be to, programos skaitymo greitis yra bent eilės tvarka didesnis nei jos rašymo greitis. Jei manote, kad visas kodas turi vienodą formatavimą (atliekama naudojant įmontuotą komandą gofmt), tada perskaityti keletą papildomų eilučių nėra jokių problemų.

Nelabai išraiškingas

Menas netoleruoja, kai jo laisvė yra suvaržyta. Tikslumas nėra jo atsakomybė.

Dėl paprastumo troškimo Go trūksta konstrukcijų, kurias kitomis kalbomis prie jų pripratę žmonės suvokia kaip kažką natūralaus. Iš pradžių tai gali būti šiek tiek nepatogu, bet tada jūs pastebėsite, kad programa yra daug lengviau ir nedviprasmiška skaityti.

Pavyzdžiui, konsolės įrankis, nuskaitantis stdin arba failą iš komandinės eilutės argumentų, atrodytų taip:

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

Tos pačios problemos sprendimas D, nors ir atrodo kiek trumpesnis, nėra lengviau skaitomas

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

Pragaras kopijavimas

Žmogus nešiojasi savyje pragarą. Martynas Liuteris.

Pradedantieji nuolat skundžiasi Go, kad trūksta generinių vaistų. Norėdami išspręsti šią problemą, dauguma jų naudoja tiesioginį kodo kopijavimą. Pavyzdžiui, sveikųjų skaičių sąrašo sumavimo funkcija, tokie būsimi profesionalai mano, kad funkcionalumas negali būti įgyvendintas jokiu kitu būdu, tik naudojant paprastą kiekvieno duomenų tipo kopijavimą-įklijavimą.

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

Kalba turi pakankamai priemonių tokioms konstrukcijoms įgyvendinti. Pavyzdžiui, bendras programavimas būtų gerai.

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

Ir nors mūsų kodas pasirodė šiek tiek ilgesnis nei ankstesnis atvejis, jis tapo apibendrintas. Todėl mums nebus sunku įgyvendinti visas aritmetines operacijas.

Daugelis sakys, kad programa D atrodo žymiai trumpesnė, ir jie bus teisūs.

import std.stdio;
import std.algorithm;

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

Tačiau jis yra tik trumpesnis, bet ne teisingesnis, nes D įgyvendinimas visiškai nepaiso klaidų tvarkymo problemos.

Realiame gyvenime, didėjant logikos sudėtingumui, atotrūkis greitai mažėja. Spragas dar greičiau užsidaro, kai reikia atlikti veiksmą, kurio negalima atlikti naudojant standartinius kalbos operatorius.

Kalbant apie prižiūrėjimą, išplečiamumą ir skaitomumą, mano nuomone, Go kalba laimi, nors pralaimi žodiškumu.

Apibendrintas programavimas kai kuriais atvejais suteikia mums neabejotinos naudos. Tai aiškiai iliustruoja rūšiavimo paketas. Taigi, norėdami surūšiuoti bet kurį sąrašą, tereikia įdiegti sąsają 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)
}

Jei pasirinksite bet kurį atvirojo kodo projektą ir paleisite komandą grep "interface{}" -R, pamatysite, kaip dažnai naudojamos paini sąsajos. Artimi bendražygiai iš karto pasakys, kad visa tai dėl generinių vaistų trūkumo. Tačiau taip būna ne visada. Paimkime DELPHI kaip pavyzdį. Nepaisant tų pačių bendrinių, jame yra specialus VARIANT tipas, skirtas operacijoms su savavališkais duomenų tipais. Go kalba daro tą patį.

Nuo patrankos iki žvirblių

O tramdomoji marškinėlė turi atitikti beprotybės dydį. Stanislavas Lec.

Daugelis ekstremalių gerbėjų gali teigti, kad „Go“ turi kitą generinių vaistų kūrimo mechanizmą – refleksiją. Ir jie bus teisūs... bet tik retais atvejais.

Robas Pike perspėja mus:

Tai galingas įrankis, kurį reikia naudoti atsargiai. To reikėtų vengti, nebent tai griežtai būtina.

Vikipedija mums sako:

Refleksija reiškia procesą, kurio metu programa gali stebėti ir keisti savo struktūrą ir elgesį vykdymo metu. Programavimo paradigma, kuria grindžiama refleksija, vadinama reflektyviuoju programavimu. Tai yra metaprogramavimo tipas.

Tačiau, kaip žinia, už viską reikia mokėti. Šiuo atveju tai yra:

  • sunkumų rašant programas
  • programos vykdymo greitis

Todėl atspindį reikia naudoti atsargiai, kaip ir didelio kalibro ginklą. Neapgalvotas refleksijos naudojimas veda prie neįskaitomų programų, nuolatinių klaidų ir mažo greičio. Kaip tik snobui programuotojui, kad jis galėtų parodyti savo kodą prieš kitus, pragmatiškesnius ir kuklesnius kolegas.

Kultūrinis bagažas iš Xi? Ne, iš kelių kalbų!

Kartu su turtu paveldėtojams paliekamos ir skolos.

Nepaisant to, kad daugelis mano, kad kalba yra visiškai pagrįsta C paveldu, taip nėra. Kalba apima daugybę geriausių programavimo kalbų aspektų.

sintaksė

Visų pirma, gramatinių struktūrų sintaksė remiasi C kalbos sintakse. Tačiau didelę įtaką turėjo ir DELPHI kalba. Taigi matome, kad pertekliniai skliaustai, kurie labai sumažina programos skaitomumą, buvo visiškai pašalinti. Kalboje taip pat yra „:=“ operatorius, būdingas DELPHI kalbai. Paketų sąvoka pasiskolinta iš tokių kalbų kaip ADA. Nenaudojamų objektų deklaracija pasiskolinta iš PROLOG kalbos.

Semantika

Paketai buvo pagrįsti DELPHI kalbos semantika. Kiekviename pakete yra duomenys ir kodas, jame yra privatūs ir viešieji subjektai. Tai leidžia sumažinti paketo sąsają iki minimumo.

Diegimo operacija delegavimo metodu buvo pasiskolinta iš DELPHI kalbos.

Kompiliacija

Ne veltui juokaujama: „Go“ buvo sukurta, kol buvo kompiliuojama C programa. Viena iš šios kalbos privalumų yra jos itin greitas kompiliavimas. Idėja pasiskolinta iš DELPHI kalbos. Kiekvienas „Go“ paketas atitinka DELPHI modulį. Šie paketai perkompiliuojami tik tada, kai tikrai reikia. Todėl po kito redagavimo nereikia kompiliuoti visos programos, o perkompiliuoti tik pakeistus paketus ir paketus, kurie priklauso nuo šių pakeistų paketų (ir net tada, jei pasikeitė paketų sąsajos).

Aukšto lygio konstrukcijos

Kalboje yra daug skirtingų aukšto lygio konstrukcijų, kurios niekaip nesusijusios su žemo lygio kalbomis, tokiomis kaip C.

  • Stygos
  • Maišos lentelės
  • Riekelės
  • Anties spausdinimas yra pasiskolintas iš tokių kalbų kaip RUBY (kurios, deja, daugelis nesupranta arba nenaudoja viso savo potencialo).

Atminties valdymas

Atminties valdymas paprastai nusipelno atskiro straipsnio. Jei tokiomis kalbomis kaip C++ valdymas visiškai paliekamas kūrėjui, tai vėlesnėse kalbose, pvz., DELPHI, buvo naudojamas atskaitos skaičiavimo modelis. Taikant šį metodą, ciklinės nuorodos nebuvo leidžiamos, nes buvo suformuotos našlaičių grupės, todėl „Go“ turi integruotą tokių grupių (pvz., C#) aptikimą. Be to, šiukšlių rinktuvas yra efektyvesnis nei dauguma šiuo metu žinomų diegimų ir jau gali būti naudojamas daugeliui užduočių realiuoju laiku. Pati kalba atpažįsta situacijas, kai krūvoje galima priskirti reikšmę kintamajam saugoti. Tai sumažina atminties tvarkyklės apkrovą ir padidina programos greitį.

Lygiagretumas ir sutapimas

Kalbos lygiagretumas ir konkurencingumas negirtinas. Jokia žemo lygio kalba net iš tolo negali konkuruoti su „Go“. Teisybės dėlei verta paminėti, kad modelį sugalvojo ne kalbos autoriai, o tiesiog pasiskolintas iš senos geros ADA kalbos. Kalba gali apdoroti milijonus lygiagrečių jungčių, naudodama visus procesorius, ir turi daug mažiau sudėtingų problemų, susijusių su aklavietėmis ir lenktynių sąlygomis, kurios būdingos kelių gijų kodui.

Papildomi privalumai

Jei tai bus pelninga, visi taps nesavanaudiški.

Kalba taip pat suteikia mums daug neabejotinų pranašumų:

  • Vienas vykdomasis failas sukūrus projektą labai supaprastina programų diegimą.
  • Statinis spausdinimas ir tipo išvados gali žymiai sumažinti klaidų skaičių jūsų kode, net ir nerašant testų. Žinau keletą programuotojų, kurie išvis apsieina be testų rašymo ir jų kodo kokybė labai nenukenčia.
  • Labai paprastas kryžminis kompiliavimas ir puikus standartinės bibliotekos perkeliamumas, o tai labai supaprastina kelių platformų programų kūrimą.
  • RE2 reguliariosios išraiškos yra saugios gijos ir turi nuspėjamą vykdymo laiką.
  • Galinga standartinė biblioteka, leidžianti daugumą projektų atlikti be trečiųjų šalių sistemų.
  • Kalba pakankamai galinga, kad sutelktų dėmesį į problemą, o ne kaip ją išspręsti, tačiau pakankamai žemo lygio, kad problemą būtų galima veiksmingai išspręsti.
  • Go eco sistemoje jau yra sukurti įrankiai visoms progoms: bandymai, dokumentacija, paketų valdymas, galingi linteriai, kodų generavimas, lenktynių sąlygų detektorius ir kt.
  • „Go“ 1.11 versijoje įdiegtas įtaisytas semantinės priklausomybės valdymas, sukurtas naudojant populiarią VCS prieglobą. Visi įrankiai, sudarantys „Go“ ekosistemą, naudoja šias paslaugas, kad vienu ypu atsisiųstų, sukurtų ir įdiegtų jų kodą. Ir tai puiku. Pasirodžius 1.11 versijai, paketo versijų kūrimo problema taip pat buvo visiškai išspręsta.
  • Kadangi pagrindinė kalbos idėja yra sumažinti magiją, kalba skatina kūrėjus aiškiai tvarkyti klaidas. Ir tai yra teisinga, nes priešingu atveju jis tiesiog pamirš apie klaidų tvarkymą. Kitas dalykas yra tai, kad dauguma kūrėjų sąmoningai ignoruoja klaidų tvarkymą, o vietoj jų apdorojimo renkasi tiesiog persiųsti klaidą aukštyn.
  • Kalba neįgyvendina klasikinės OOP metodikos, nes gryna forma Go nėra virtualumo. Tačiau naudojant sąsajas tai nėra problema. OOP nebuvimas žymiai sumažina pradedantiesiems patekti į rinką.

Paprastumas bendruomenės labui

Lengva komplikuoti, sunku supaprastinti.

„Go“ buvo sukurta taip, kad būtų paprasta, ir jam pavyksta pasiekti šį tikslą. Jis buvo parašytas protingiems programuotojams, kurie supranta komandinio darbo naudą ir yra pavargę nuo begalinio Enterprise lygio kalbų kintamumo. Turėdamas gana nedidelį sintaksinių struktūrų rinkinį savo arsenale, jis praktiškai nesikeičia laikui bėgant, todėl kūrėjai turi daug laiko atlaisvinti plėtrai, o ne be galo tyrinėti kalbos naujoves.

Įmonės taip pat gauna nemažai privalumų: mažas įėjimo barjeras leidžia greitai susirasti specialistą, o kalbos nekintamumas leidžia naudoti tą patį kodą net po 10 metų.

išvada

Dėl didelio smegenų dydžio dramblys niekada nebuvo Nobelio premijos laureatas.

Tiems programuotojams, kurių asmeninis ego yra svarbesnis už komandinę dvasią, taip pat teoretikams, mėgstantiems akademinius iššūkius ir begalinį „savęs tobulėjimą“, kalba tikrai prasta, nes tai bendrosios paskirties amatininkų kalba, neleidžianti estetinį malonumą iš savo darbo rezultato ir parodykite save profesionaliai prieš kolegas (su sąlyga, kad intelektą matuojame šiais kriterijais, o ne IQ). Kaip ir viskas gyvenime, tai yra asmeninių prioritetų reikalas. Kaip ir visos vertos naujovės, kalba jau nuėjo ilgą kelią nuo visuotinio neigimo iki masinio pripažinimo. Kalba išradinga savo paprastumu, o, kaip žinia, viskas, kas išradinga, yra paprasta!

Šaltinis: www.habr.com

Добавить комментарий