Zašto je Go loš za nepametne programere

Članak je nastao kao odgovor na prethodno objavljeno antipodni članak.

Zašto je Go loš za nepametne programere

U posljednje dvije i više godine koristio sam Go za implementaciju specijaliziranog RADIUS poslužitelja s razvijenim sustavom naplate. Usput učim zamršenosti samog jezika. Sami programi su vrlo jednostavni i nisu svrha ovog članka, ali samo iskustvo korištenja Goa zaslužuje nekoliko riječi u svoju obranu. Go postaje sve više mainstream jezik za ozbiljan, skalabilni kod. Jezik je kreirao Google, gdje se aktivno koristi. Ukratko, iskreno mislim da je dizajn Go jezika loš za UNinteligentne programere.

Dizajniran za slabe programere?

Slabi govore o problemima. Snažan razgovor o idejama i snovima...

Go je vrlo jednostavan za naučiti, toliko jednostavan da možete pročitati kod gotovo bez ikakve obuke. Ova značajka jezika koristi se u mnogim globalnim tvrtkama kada se kod čita zajedno sa sporednim stručnjacima (menadžerima, kupcima itd.). Ovo je vrlo zgodno za metodologije kao što je razvoj vođen dizajnom.
Čak i programeri početnici počnu proizvoditi sasvim pristojan kod nakon tjedan ili dva. Knjiga iz koje sam učio je “Go Programming” (od Marka Summerfielda). Knjiga je vrlo dobra, dotiče mnoge nijanse jezika. Nakon nepotrebno kompliciranih jezika kao što su Java, PHP, nedostatak magije je osvježenje. Ali prije ili kasnije, mnogi ograničeni programeri imaju ideju o korištenju starih metoda u novom području. Je li ovo stvarno potrebno?

Rob Pike (glavni ideolog jezika) stvorio je Go jezik kao industrijski jezik koji je lako razumljiv i učinkovit za korištenje. Jezik je dizajniran za maksimalnu produktivnost u velikim timovima iu to nema sumnje. Mnogi programeri početnici žale se da im nedostaju mnoge značajke. Ova želja za jednostavnošću bila je svjesna odluka dizajnera jezika, a kako bismo u potpunosti razumjeli zašto je to bilo potrebno, moramo razumjeti motivaciju programera i ono što su pokušavali postići u Gou.

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

Ključna stvar ovdje je da naši programeri nisu istraživači. Oni su u pravilu prilično mladi, dolaze kod nas nakon studija, možda su učili Javu, ili C/C++, ili Python. Oni ne razumiju dobar jezik, ali u isto vrijeme želimo da kreiraju dobar softver. Zato jezik treba biti lak za razumijevanje i učenje.

Trebao bi biti poznat, otprilike sličan C-u. Programeri koji rade u Googleu rano započinju svoje karijere i uglavnom su upoznati s proceduralnim jezicima, posebice obitelji 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 nužan uvjet ljepote. Lav Tolstoj.

Održavanje jednostavnosti jedan je od najvažnijih ciljeva u svakom dizajnu. Kao što znate, savršen projekt nije onaj kojem se nema što dodati, već onaj kojem se nema što ukloniti. Mnogi ljudi vjeruju da je za rješavanje (ili čak izražavanje) složenih problema potreban složen alat. Međutim, nije. Uzmimo za primjer jezik PERL. Jezični ideolozi vjerovali su da programer treba imati najmanje tri različita načina za rješavanje jednog problema. Ideolozi Go jezika krenuli su drugim putem, odlučili su da je za postizanje cilja dovoljan jedan način, ali jako dobar. Ovaj pristup ima ozbiljne temelje: jedini način je lakše naučiti, a teže zaboraviti.

Mnogi se migranti žale da jezik ne sadrži elegantne apstrakcije. Da, to je istina, ali to je jedna od glavnih prednosti jezika. Jezik sadrži minimum magije - tako da nije potrebno duboko znanje za čitanje programa. Što se tiče opširnosti koda, to uopće nije problem. Dobro napisan Golang program čita okomito, s malo ili nimalo strukture. Osim toga, brzina čitanja programa je barem jedan red veličine veća od brzine pisanja. Ako uzmete u obzir da sav kod ima jednoobrazno oblikovanje (obavljeno pomoću ugrađene naredbe gofmt), onda čitanje nekoliko dodatnih redaka uopće ne predstavlja problem.

Ne baš izražajno

Umjetnost ne trpi kada joj se ograničava sloboda. Točnost nije njegova odgovornost.

Zbog želje za jednostavnošću, Gou nedostaju konstrukti koje u drugim jezicima ljudi koji su navikli na njih doživljavaju kao nešto prirodno. U početku može biti malo 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 naredbenog retka izgledao bi 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 ništa 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);
    }
}

Vraga kopiranja

Čovjek u sebi nosi pakao. Martin Luther.

Početnici se stalno žale na Go u smislu nedostatka generičkih proizvoda. Kako bi riješili ovaj problem, većina njih koristi izravno kopiranje koda. Na primjer, funkcija za zbrajanje popisa cijelih brojeva, takvi potencijalni profesionalci vjeruju da se funkcionalnost ne može implementirati ni na koji drugi način osim jednostavnim kopiranjem i lijepljenjem za svaku vrstu 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 naš kod pokazao nešto dužim 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će i bit ć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, on je samo kraći, ali ne i ispravniji, budući da D implementacija potpuno zanemaruje problem rukovanja greškama.

U stvarnom životu, kako se složenost logike povećava, jaz se brzo smanjuje. Praznina se zatvara još brže kada trebate izvršiti radnju koja se ne može izvesti korištenjem 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 daje nam neosporne prednosti. To je jasno ilustrirano paketom sortiranja. Dakle, da sortiramo bilo koji popis, samo trebamo implementirati sučelje 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)
}

Ako uzmete bilo koji projekt otvorenog koda i pokrenete naredbu grep “interface{}” -R, vidjet ćete koliko se često koriste zbunjujuća sučelja. Drugovi iz bliskog uma odmah će reći da je sve to zbog nedostatka generičkih lijekova. Međutim, to nije uvijek slučaj. Uzmimo DELPHI kao primjer. Unatoč prisutnosti tih istih generika, sadrži poseban tip VARIANT za operacije s proizvoljnim tipovima podataka. Go jezik čini isto.

Od topa do vrabaca

I luđačka košulja mora odgovarati veličini ludila. Stanislav Lec.

Mnogi ljubitelji ekstrema mogu tvrditi da Go ima još jedan mehanizam za stvaranje generičkih proizvoda - refleksiju. I bit će u pravu... ali samo u rijetkim slučajevima.

Rob Pike nas upozorava:

Ovo je moćan alat koji treba koristiti s oprezom. Treba ga izbjegavati osim ako nije nužno.

Wikipedia nam govori sljedeće:

Refleksija se odnosi na proces tijekom kojeg program može nadzirati i modificirati vlastitu strukturu i ponašanje tijekom izvođenja. Paradigma programiranja na kojoj se temelji refleksija naziva se refleksivno programiranje. Ovo je vrsta metaprogramiranja.

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

  • poteškoće u pisanju programa
  • brzina izvođenja programa

Stoga se refleksija mora koristiti oprezno, poput oružja velikog kalibra. Nepromišljeno korištenje refleksije dovodi do nečitljivih programa, stalnih pogrešaka i niske brzine. Taman za programera snoba da se može pohvaliti svojim kodom pred drugim, pragmatičnijim i skromnijim kolegama.

Kulturna prtljaga iz Xija? Ne, iz više jezika!

Uz bogatstvo, nasljednicima ostaju i dugovi.

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

sintaksa

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

Semantika

Paketi su bili bazirani na semantici DELPHI jezika. Svaki paket sadrži podatke i kod te sadrži privatne i javne entitete. To vam omogućuje smanjenje sučelja paketa na minimum.

Operacija implementacije metodom delegiranja posuđena je iz jezika DELPHI.

Kompilacija

Nije bez razloga postoji šala: Go je razvijen dok se kompilirao C program. Jedna od prednosti jezika je njegova ultra-brza kompilacija. Ideja je posuđena iz jezika DELPHI. Svaki Go paket odgovara DELPHI modulu. Ovi se paketi ponovno kompajliraju samo kada je stvarno potrebno. Stoga, nakon sljedećeg uređivanja, ne trebate kompajlirati cijeli program, već ponovno kompajlirati samo promijenjene pakete i pakete koji ovise o tim promijenjenim paketima (pa čak i tada, samo ako su se sučelja paketa promijenila).

Konstrukcije visoke razine

Jezik sadrži mnogo različitih konstrukcija visoke razine koje ni na koji način nisu povezane s jezicima niske razine kao što je C.

  • Žice
  • Hash tablice
  • Kriške
  • Duck tipkanje je posuđeno iz jezika kao što je RUBY (koji, nažalost, mnogi ne razumiju ili ne koriste u punom potencijalu).

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 kao što je DELPHI korišten model brojanja referenci. S ovim pristupom, cikličke reference nisu bile dopuštene, budući da su formirani klasteri siroče, tada Go ima ugrađenu detekciju takvih klastera (poput C#). Osim toga, skupljač smeća je učinkovitiji od većine trenutno poznatih implementacija i već se može koristiti za mnoge zadatke u stvarnom vremenu. Sam jezik prepoznaje situacije kada se vrijednost za pohranjivanje varijable može dodijeliti na stogu. To smanjuje opterećenje upravitelja memorije i povećava brzinu programa.

Konkurentnost i Konkurentnost

Paralelnost i konkurentnost jezika je za svaku pohvalu. Nijedan jezik niske razine ne može se ni izdaleka natjecati s Goom. Da budemo pošteni, vrijedi napomenuti da model nisu izmislili autori jezika, već je jednostavno posuđen iz dobrog starog jezika ADA. Jezik je sposoban obraditi milijune paralelnih veza korištenjem svih CPU-a, dok ima red veličine manje složenih problema sa zastojima i uvjetima utrke koji su tipični za kod s više niti.

Dodatne pogodnosti

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

Jezik nam također pruža niz nedvojbenih prednosti:

  • Jedna izvršna datoteka nakon izgradnje projekta uvelike pojednostavljuje implementaciju aplikacija.
  • Statičko tipkanje i zaključivanje tipa mogu značajno smanjiti broj pogrešaka u vašem kodu, čak i bez pisanja testova. Poznajem neke programere koji uopće ne pišu testove i kvaliteta njihovog koda ne trpi značajno.
  • Vrlo jednostavna cross-kompilacija i izvrsna prenosivost standardne biblioteke, što uvelike pojednostavljuje razvoj cross-platform aplikacija.
  • RE2 regularni izrazi sigurni su za niti i imaju predvidljiva vremena izvršenja.
  • Snažna standardna biblioteka koja većini projekata omogućuje rad bez okvira trećih strana.
  • Jezik je dovoljno moćan da se usredotoči na problem, a ne na to kako ga riješiti, ali dovoljno niske razine da se problem može učinkovito riješiti.
  • Sustav Go eco već sadrži razvijene alate izvan kutije za sve prilike: testove, dokumentaciju, upravljanje paketima, moćne lintere, generiranje koda, detektor uvjeta utrke itd.
  • Go verzija 1.11 uvela je ugrađeno upravljanje semantičkom ovisnošću, izgrađeno na vrhu popularnog VCS hostinga. Svi alati koji čine Go ekosustav koriste ove usluge za preuzimanje, izgradnju i instaliranje koda iz njih jednim potezom. I to je super. Dolaskom verzije 1.11 potpuno je riješen i problem s verzioniranjem paketa.
  • Budući da je temeljna ideja jezika smanjiti magiju, jezik potiče programere da eksplicitno rješavaju pogreške. I to je točno, jer će u protivnom jednostavno potpuno zaboraviti na obradu grešaka. Druga stvar je da većina programera namjerno ignorira rukovanje pogreškama, radije umjesto da ih obrađuju da jednostavno proslijede pogrešku prema gore.
  • Jezik ne implementira klasičnu OOP metodologiju, jer u svom čistom obliku nema virtualnosti u Go. Međutim, to nije problem pri korištenju sučelja. Nepostojanje OOP-a značajno smanjuje prepreku ulasku za početnike.

Jednostavnost za dobrobit zajednice

Lako je zakomplicirati, 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 razini poduzeća. Imajući relativno mali skup sintaktičkih struktura u svom arsenalu, praktički nije podložan promjenama tijekom vremena, tako da programeri imaju puno vremena oslobođenog za razvoj, a ne za beskrajno proučavanje jezičnih inovacija.

Tvrtke također dobivaju brojne prednosti: niska ulazna barijera omogućuje im brzo pronalaženje stručnjaka, a nepromjenjivost jezika omogućuje im korištenje istog koda čak i nakon 10 godina.

Zaključak

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

Za one programere čiji osobni ego ima prednost nad timskim duhom, kao i teoretičare koji vole akademske izazove i beskrajno "samousavršavanje", jezik je stvarno loš, budući da je to zanatski jezik opće namjene koji vam ne dopušta da dobijete estetski užitak od rezultata rada i pokazati se profesionalnim pred kolegama (pod uvjetom da inteligenciju mjerimo ovim kriterijima, a ne IQ-om). Kao i sve u životu, stvar je osobnih prioriteta. Kao i sve vrijedne inovacije, jezik je već prošao dug put od univerzalnog poricanja do masovnog prihvaćanja. Jezik je genijalan u svojoj jednostavnosti, a, kao što znate, sve genijalno je jednostavno!

Izvor: www.habr.com

Dodajte komentar