Zašto je Go loš za nepametne programere

Članak je napisan kao odgovor na prethodno objavljeno antipodski članak.

Zašto je Go loš za nepametne programere

U protekle dvije i više godine koristio sam Go za implementaciju specijalizovanog RADIUS servera sa razvijenim sistemom naplate. Usput, učim zamršenosti samog jezika. Sami programi su vrlo jednostavni i nisu svrha ovog članka, ali samo iskustvo korištenja Go-a zaslužuje nekoliko riječi u svoju odbranu. Go postaje sve popularniji jezik za ozbiljan, skalabilan kod. Jezik je kreirao Google, gdje se aktivno koristi. U krajnjoj liniji, iskreno mislim da je dizajn Go jezika loš za UNinteligentne programere.

Dizajniran za slabe programere?

Slabiji govore o problemima. Snažan govor o idejama i snovima...

Go je vrlo jednostavan za učenje, tako jednostavan da možete čitati kod bez ikakve obuke. Ova karakteristika jezika se koristi u mnogim globalnim kompanijama kada se kod čita zajedno sa neosnovnim stručnjacima (menadžeri, kupci, itd.). Ovo je vrlo zgodno za metodologije kao što je razvoj vođen dizajnom.
Čak i programeri početnici počinju da proizvode sasvim pristojan kod nakon nedelju ili dve. Knjiga iz koje sam učio je “Go Programming” (od Marka Summerfielda). Knjiga je jako dobra, dotiče mnoge nijanse jezika. Nakon nepotrebno komplikovanih jezika kao što su Java, PHP, nedostatak magije je osvježavajući. Ali prije ili kasnije, mnogi ograničeni programeri imaju ideju da koriste stare metode u novom polju. Da li je ovo zaista neophodno?

Rob Pike (glavni ideolog jezika) stvorio je Go jezik kao industrijski jezik koji je lak za razumevanje i efikasan za upotrebu. Jezik je dizajniran za maksimalnu produktivnost u velikim timovima i u to nema sumnje. Mnogi programeri početnici žale se da im nedostaje mnogo funkcija. Ova želja za jednostavnošću bila je svjesna odluka dizajnera jezika, a da bismo u potpunosti razumjeli zašto je to bilo potrebno, moramo razumjeti motivaciju programera i šta su oni pokušavali postići u Go-u.

Pa zašto je to tako jednostavno? Evo nekoliko citata Roba Pikea:

Ključna stvar je da naši programeri nisu istraživači. Oni su, po pravilu, prilično mladi, dolaze nam nakon studija, možda su učili Javu, ili C/C++, ili Python. Ne razumiju sjajan jezik, ali u isto vrijeme želimo da kreiraju dobar softver. Zato jezik treba da bude lak za razumevanje i učenje.

On bi trebao biti upoznat, grubo rečeno sličan C. Programeri koji rade u Google-u rano započinju svoju karijeru i uglavnom su upoznati sa proceduralnim jezicima, posebno sa porodicom C. Zahtjev za brzom produktivnošću u novom programskom jeziku znači da jezik ne bi trebao biti previše radikalan.

Mudre riječi, zar ne?

Artefakti jednostavnosti

Jednostavnost je neophodan uslov za lepotu. Lev Tolstoj.

Održavanje jednostavnosti jedan je od najvažnijih ciljeva u svakom dizajnu. Kao što znate, savršen projekat nije projekat u kome nema šta da se doda, već onaj od koga nema šta da se ukloni. Mnogi ljudi vjeruju da je za rješavanje (ili čak izražavanje) složenih problema potreban kompleksan alat. Međutim, nije. Uzmimo za primjer jezik PERL. Jezički ideolozi vjerovali su da programer treba da ima najmanje tri različita načina da riješi jedan problem. Ideolozi jezika Go krenuli su drugim putem, odlučili su da je jedan način, ali zaista dobar, dovoljan za postizanje cilja. Ovaj pristup ima ozbiljnu osnovu: jedini način je lakše naučiti i teže zaboraviti.

Mnogi migranti se žale da jezik ne sadrži elegantne apstrakcije. Da, to je tačno, ali to je jedna od glavnih prednosti jezika. Jezik sadrži minimum magije - tako da za čitanje programa nije potrebno duboko znanje. Što se tiče opširnosti koda, to uopće nije problem. Dobro napisan Golang program čita vertikalno, sa malo ili bez strukture. Osim toga, brzina čitanja programa je barem za red veličine veća od brzine pisanja. Ako uzmete u obzir da sav kod ima uniformno formatiranje (urađeno pomoću ugrađene komande gofmt), onda čitanje nekoliko dodatnih redova uopće nije problem.

Nije baš ekspresivno

Umjetnost ne toleriše kada je njena sloboda ograničena. Tačnost nije njegova odgovornost.

Zbog želje za jednostavnošću, Gou nedostaju konstrukti koje ljudi navikli na njih u drugim jezicima doživljavaju kao nešto prirodno. U početku može biti pomalo nezgodno, ali onda primijetite da je program mnogo lakši i nedvosmisleniji za čitanje.

Na primjer, uslužni program konzole koji čita stdin ili datoteku iz argumenata komandne linije bi izgledao ovako:

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

Rješenje istog problema u D, iako izgleda nešto kraće, nije lakše za čitanje

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

Pakao od kopiranja

Čovek nosi pakao u sebi. Martin Luther.

Početnici se stalno žale na Go u smislu nedostatka generika. Da bi riješili ovaj problem, većina njih koristi direktno kopiranje koda. Na primjer, funkcija za zbrajanje liste cijelih brojeva, takvi potencijalni profesionalci vjeruju da se funkcionalnost ne može implementirati na bilo koji drugi način osim jednostavnim kopiranjem za svaki tip podataka.

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

Jezik ima dovoljno sredstava za implementaciju takvih konstrukcija. Na primjer, generičko programiranje bi bilo u redu.

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

I, iako se pokazalo da je naš kod nešto duži od prethodnog slučaja, postao je generaliziran. Stoga nam neće biti teško implementirati sve aritmetičke operacije.

Mnogi će reći da program u D izgleda znatno kraći i biće u pravu.

import std.stdio;
import std.algorithm;

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

Međutim, samo je kraći, ali ne i ispravniji, budući da D implementacija potpuno zanemaruje problem rukovanja greškama.

U stvarnom životu, kako se kompleksnost logike povećava, jaz se brzo sužava. Jaz se još brže zatvara kada trebate izvršiti radnju koja se ne može izvesti pomoću operatora standardnog jezika.

U pogledu mogućnosti održavanja, proširivosti i čitljivosti, po mom mišljenju, Go jezik pobjeđuje, iako gubi u opširnosti.

Generalizirano programiranje u nekim slučajevima nam daje neosporne prednosti. Ovo je jasno ilustrovano paketom sortiranja. Dakle, da bismo sortirali bilo koju listu, samo trebamo implementirati sort.Interface interfejs.

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

Ako uzmete bilo koji projekat otvorenog koda i pokrenete komandu grep “interface{}” -R, vidjet ćete koliko se često koriste zbunjujući interfejsi. Bliskoumni drugovi će odmah reći da je sve to zbog nedostatka generika. Međutim, to nije uvijek slučaj. Uzmimo DELPHI kao primjer. Uprkos prisustvu ovih istih generika, on sadrži poseban tip VARIANT za operacije sa proizvoljnim tipovima podataka. Go jezik radi isto.

Od topa do vrapca

I ludačka košulja mora odgovarati veličini ludila. Stanislav Lec.

Mnogi ekstremni fanovi mogu tvrditi da Go ima još jedan mehanizam za kreiranje generika - refleksiju. I biće u pravu... ali samo u retkim slučajevima.

Rob Pike nas upozorava:

Ovo je moćan alat koji treba koristiti s oprezom. Treba ga izbjegavati osim ako je to strogo neophodno.

Wikipedia nam govori sljedeće:

Refleksija se odnosi na proces tokom kojeg program može pratiti i modificirati vlastitu strukturu i ponašanje tokom izvršavanja. Paradigma programiranja koja leži u osnovi refleksije naziva se refleksivno programiranje. Ovo je vrsta metaprogramiranja.

Međutim, kao što znate, sve morate platiti. U ovom slučaju to je:

  • poteškoće u pisanju programa
  • brzina izvršavanja programa

Stoga se refleksija mora koristiti s oprezom, poput oružja velikog kalibra. Nepromišljena upotreba refleksije dovodi do nečitljivih programa, konstantnih grešaka i male brzine. Baš ono za snob programera da može da pokaže svoj kod pred drugim, pragmatičnijim i skromnijim kolegama.

Kulturni prtljag od Xija? Ne, sa više jezika!

Uz bogatstvo, dugovi su ostavljeni i nasljednicima.

Uprkos činjenici da mnogi vjeruju da je jezik u potpunosti zasnovan na nasljeđu C, to nije slučaj. Jezik uključuje mnoge aspekte najboljih programskih jezika.

sintaksa

Prije svega, sintaksa gramatičkih struktura zasniva se na sintaksi jezika C. Međutim, DELPHI jezik je takođe imao značajan uticaj. Tako vidimo da su suvišne zagrade, koje uvelike smanjuju čitljivost programa, potpuno uklonjene. Jezik takođe sadrži operator “:=” svojstven DELPHI jeziku. Koncept paketa je posuđen iz jezika poput ADA. Deklaracija neiskorištenih entiteta je posuđena iz PROLOG jezika.

Semantika

Paketi su zasnovani na semantici DELPHI jezika. Svaki paket sadrži podatke i kod i sadrži privatne i javne entitete. Ovo vam omogućava da smanjite interfejs paketa na minimum.

Operacija implementacije metodom delegacije je pozajmljena iz DELPHI jezika.

Kompilacija

Nije bez razloga šala: Go je razvijen dok se kompajlira C program. Jedna od prednosti jezika je njegova ultra-brza kompilacija. Ideja je pozajmljena iz DELPHI jezika. Svaki Go paket odgovara DELPHI modulu. Ovi paketi se ponovo kompajliraju samo kada je to zaista neophodno. Stoga, nakon sljedećeg uređivanja, ne morate kompajlirati cijeli program, već ponovo kompajlirati samo izmijenjene pakete i pakete koji zavise od ovih promijenjenih paketa (pa čak i tada, samo ako su se promijenila sučelja paketa).

Konstrukcije visokog nivoa

Jezik sadrži mnogo različitih konstrukcija visokog nivoa koji ni na koji način nisu povezani sa jezicima niskog nivoa kao što je C.

  • Strings
  • Hash tabele
  • Slices
  • Pačje kucanje je posuđeno iz jezika kao što je RUBY (koji, nažalost, mnogi ne razumiju ili koriste u potpunosti).

Upravljanje memorijom

Upravljanje memorijom općenito zaslužuje poseban članak. Ako je u jezicima kao što je C++ kontrola u potpunosti prepuštena programeru, onda je u kasnijim jezicima poput DELPHI korišten model brojanja referenci. Ovim pristupom, cikličke reference nisu bile dozvoljene, pošto su formirani klasteri siročadi, tada Go ima ugrađenu detekciju takvih klastera (kao C#). Uz to, sakupljač smeća je efikasniji od većine trenutno poznatih implementacija i već se može koristiti za mnoge zadatke u realnom vremenu. Sam jezik prepoznaje situacije kada se vrijednost za pohranjivanje varijable može dodijeliti na stog. Ovo smanjuje opterećenje upravitelja memorije i povećava brzinu programa.

Konkurencija i istovremenost

Paralelnost i konkurentnost jezika je za svaku pohvalu. Nijedan jezik niskog nivoa ne može čak ni iz daljine da se takmiči sa Go. Iskreno rečeno, vrijedno je napomenuti da model nisu izmislili autori jezika, već je jednostavno posuđen iz dobrog starog ADA jezika. Jezik je sposoban da obrađuje milione paralelnih veza koristeći sve CPU, dok ima manje složene probleme sa zastojima i uslovima trke koji su tipični za višenitni kod.

Dodatne pogodnosti

Ako je profitabilno, svi će postati nesebični.

Jezik nam takođe pruža niz nesumnjivih prednosti:

  • Jedna izvršna datoteka nakon izgradnje projekta uvelike pojednostavljuje primenu aplikacija.
  • Statičko kucanje i zaključivanje tipa mogu značajno smanjiti broj grešaka u vašem kodu, čak i bez pisanja testova. Poznajem neke programere koji uopće ne pišu testove i kvalitet njihovog koda ne trpi značajno.
  • Vrlo jednostavna unakrsna kompilacija i odlična prenosivost standardne biblioteke, što uvelike pojednostavljuje razvoj međuplatformskih aplikacija.
  • RE2 regularni izrazi su sigurni u niti i imaju predvidljiva vremena izvršavanja.
  • Moćna standardna biblioteka koja omogućava većini projekata da rade bez okvira treće strane.
  • Jezik je dovoljno moćan da se fokusira na problem, a ne na to kako ga riješiti, ali dovoljno niskog nivoa da se problem može riješiti efikasno.
  • Go eco sistem već sadrži razvijene alate za sve prilike: testove, dokumentaciju, upravljanje paketima, moćne lintere, generisanje koda, detektor uslova trke, itd.
  • Go verzija 1.11 uvela je ugrađeno upravljanje semantičkim ovisnostima, izgrađeno na vrhu popularnog VCS hostinga. Svi alati koji čine Go ekosistem koriste ove usluge za preuzimanje, izgradnju i instaliranje koda sa njih u jednom potezu. I to je super. Dolaskom verzije 1.11, problem sa verzioniranjem paketa je također u potpunosti riješen.
  • Budući da je osnovna ideja jezika smanjenje magije, jezik podstiče programere da eksplicitno obrađuju greške. I to je tačno, jer će u suprotnom jednostavno potpuno zaboraviti na rukovanje greškama. Druga stvar je da većina programera namjerno zanemaruje rukovanje greškama, preferirajući umjesto da ih obrađuje da jednostavno proslijede grešku prema gore.
  • Jezik ne implementira klasičnu OOP metodologiju, pošto u svom čistom obliku nema virtuelnosti u Go. Međutim, to nije problem kada se koriste interfejsi. Odsustvo OOP-a značajno smanjuje barijeru za ulazak početnika.

Jednostavnost za dobrobit zajednice

Lako je zakomplikovati, teško pojednostaviti.

Go je dizajniran da bude jednostavan i uspijeva u tom cilju. Napisan je za pametne programere koji razumiju prednosti timskog rada i umorni su od beskrajne varijabilnosti jezika na nivou preduzeća. Imajući relativno mali skup sintaktičkih struktura u svom arsenalu, praktički nije podložan promjenama tokom vremena, tako da programeri imaju puno vremena oslobođeno za razvoj, a ne za beskonačno proučavanje jezičnih inovacija.

Kompanije također dobijaju niz prednosti: niska ulazna barijera omogućava im da brzo pronađu stručnjaka, a nepromjenjivost jezika im omogućava da koriste isti kod čak i nakon 10 godina.

zaključak

Velika veličina mozga nikada nije učinila nijednog slona dobitnikom Nobelove nagrade.

Za one programere čiji lični ego ima prednost nad timskim duhom, kao i teoretičare koji vole akademske izazove i beskonačno „samousavršavanje“, jezik je zaista loš, jer je riječ o zanatskom jeziku opšte namjene koji vam ne dozvoljava da dobijete estetski užitak od rezultata svog rada i pokazati se profesionalnim pred kolegama (pod uslovom da inteligenciju mjerimo ovim kriterijima, a ne IQ). Kao i sve u životu, i to je stvar ličnih prioriteta. Kao i sve vrijedne inovacije, jezik je već prešao dug put od univerzalnog poricanja do masovnog prihvatanja. Jezik je genijalan u svojoj jednostavnosti, a, kao što znate, sve genijalno je jednostavno!

izvor: www.habr.com

Dodajte komentar