Wêrom Go Design min is foar tûke programmeurs

De ôfrûne moannen haw ik Go brûkt foar ymplemintaasjes. Bewiis fan konsept (ca.: koade om de funksjonaliteit fan in idee te testen) yn syn frije tiid, foar in part om de programmeartaal sels te studearjen. De programma's sels binne heul ienfâldich en binne net it doel fan dit artikel, mar de ûnderfining fan it brûken fan Go sels fertsjinnet in pear wurden oer. Go belooft te wêzen (ca.: artikel skreaun yn 2015) in populêre taal foar serieuze scalable koade. De taal is makke troch Google, wêr't it aktyf brûkt wurdt. Bottom line, ik tink earlik sein dat it ûntwerp fan 'e Go-taal min is foar tûke programmeurs.

Untworpen foar swakke programmeurs?

Go is hiel maklik om te learen, sa maklik dat de ynlieding my op in jûn naam, wêrnei't ik al produktyf koade koe. It boek dat ik brûkt om te learen Go hjit In yntroduksje ta programmearring yn Go (oersetting), it is online beskikber. It boek is, lykas de Go-boarnekoade sels, maklik te lêzen, hat goede koadefoarbylden en befettet sa'n 150 siden dy't yn ien kear lêzen wurde kinne. Dizze ienfâld is earst ferrassend, foaral yn in programmearwrâld fol mei te yngewikkelde technology. Mar op it lêst komt ier of let de gedachte op: "Is dit echt sa?"

Google beweart dat de ienfâld fan Go it ferkeappunt is en de taal is ûntworpen foar maksimale produktiviteit yn grutte teams, mar ik twifelje der oan. D'r binne funksjes dy't ûntbrekke of te detaillearre binne. En alles fanwegen in gebrek oan fertrouwen yn ûntwikkelders, mei de oanname dat se neat goed kinne dwaan. Dizze winsk nei ienfâld wie in bewuste beslút troch de ûntwerpers fan 'e taal, en om folslein te begripen wêrom't it nedich wie, moatte wy de motivaasje fan 'e ûntwikkelders begripe en wat se besochten te berikken yn Go.

Dus wêrom waard it sa ienfâldich makke? Hjir binne in pear sitaten Rob Pike (ca.: ien fan 'e mei-skeppers fan 'e Go-taal):

It wichtichste punt hjir is dat ús programmeurs (ca.: Googlers) binne gjin ûndersikers. Se binne, as regel, frij jong, komme nei ús stúdzje, miskien se studearre Java, of C/C++, of Python. Se kinne gjin geweldige taal ferstean, mar tagelyk wolle wy dat se goede software meitsje. Dêrom moat har taal maklik te begripen en leare wêze.
 
Hy soe fertroud wêze moatte, rûchwei fergelykber mei C. Programmeurs dy't wurkje by Google begjinne har karriêre betiid en binne meast bekend mei prosedurele talen, benammen de C-famylje. De eask foar flugge produktiviteit yn in nije programmeartaal betsjut dat de taal net te radikaal wêze moat.

Wat? Dat Rob Pike seit yn prinsipe dat de ûntwikkelders by Google net sa goed binne, dêrom hawwe se in taal makke foar idioaten (ca.: stomme) sadat se wat dwaan kinne. Wat foar arrogante blik op dyn eigen kollega's? Ik haw altyd leaud dat de ûntwikkelders fan Google binne mei de hân keazen út de helderste en bêste op ierde. Se kinne wol wat lestigers oan?

Artefakten fan oermjittige ienfâld

Ienfâldich wêze is in weardich doel yn elk ûntwerp, en besykje wat ienfâldich te meitsjen is lestich. As jo ​​​​lykwols besykje komplekse problemen op te lossen (of sels útdrukke, is soms in kompleks ark nedich. Kompleksiteit en yngewikkeldheid binne net de bêste eigenskippen fan in programmeartaal, mar der is in middengrûn wêryn de taal elegante abstraksjes meitsje kin dy't maklik te begripen en te brûken binne.

Net hiel ekspressyf

Fanwegen syn ynset foar ienfâld mist Go konstruksjes dy't yn oare talen as natuerlik wurde waarnommen. Dit kin earst in goed idee lykje, mar yn 'e praktyk resultearret it yn verbose koade. De reden hjirfoar soe fanselssprekkend wêze moatte - it moat maklik wêze foar ûntwikkelders om de koade fan oaren te lêzen, mar feitlik skealje dizze ferienfâldigingen allinich de lêsberens. D'r binne gjin ôfkoartings yn Go: in protte of neat.

Bygelyks, in konsole-hulpprogramma dat stdin lêst as in bestân fan kommandorigelarguminten soe der sa útsjen:

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

Hoewol't dizze koade ek besiket te wêzen sa algemien mooglik, Go syn twongen verbosity stiet yn 'e wei, en as gefolch, it oplossen fan in ienfâldich probleem resultearret yn in grutte hoemannichte koade.

Hjir is bygelyks in oplossing foar itselde probleem yn 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);
    }
}

En wa is no mear lêsber? Ik sil myn stim jaan oan D. Syn koade is folle lêsber, om't hy de aksjes dúdliker beskriuwt. D brûkt folle kompleksere begripen (ca.: alternative funksje oprop и templates) as yn it Go-foarbyld, mar d'r is eins neat yngewikkeld oer it begripen fan har.

Hel fan kopiearjen

In populêre suggestje foar it ferbetterjen fan Go is algemienens. Dit sil op syn minst helpe om ûnnedich kopiearjen fan koade te foarkommen om alle gegevenstypen te stypjen. Bygelyks, in funksje foar it opteljen fan in list mei heule getallen kin op gjin oare manier ymplementearre wurde as troch de basisfunksje foar elk type ynteger te kopiearjen en plakke; der is gjin oare manier:

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

En dit foarbyld wurket net iens foar ûndertekene typen. Dizze oanpak is folslein yn striid mei it prinsipe fan josels net werhelje (DROECH), ien fan 'e meast ferneamde en foar de hân lizzende prinsipes, negearje wat de boarne is fan in protte flaters. Wêrom docht Go dit? Dit is in ferskriklik aspekt fan taal.

Itselde foarbyld op D:

import std.stdio;
import std.algorithm;

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

Ienfâldich, elegant en direkt nei it punt. De funksje brûkt hjir is reduce foar sjabloantype en predikaat. Ja, dit is wer yngewikkelder as de Go-ferzje, mar net sa dreech foar tûke programmeurs om te begripen. Hokker foarbyld is makliker te ûnderhâlden en makliker te lêzen?

Ienfâldich type systeem bypass

Ik stel my foar dat Go-programmeurs dy't dit lêze sille skuimje om 'e mûle en skrieme, "Jo dogge it ferkeard!" No, d'r is in oare manier om in generike funksje en typen te meitsjen, mar it brekt it typesysteem folslein!

Besjoch dit foarbyld fan in domme taalreparaasje om it probleem om te gean:

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

Dizze ymplemintaasje Reduce waard liend út it artikel Idiomatyske generika yn Go (ca.: Ik koe de oersetting net fine, ik sil bliid wêze as jo hjirmei helpe). No, as it idiomatysk is, soe ik in net-idiomatysk foarbyld haatsje. Gebrûk interface{} - in farce, en yn 'e taal is it allinich nedich om typen te omgean. Dit is in lege ynterface en alle soarten implementearje it, wêrtroch folsleine frijheid foar elkenien mooglik is. Dizze styl fan programmearring is ferskriklik ûnsjoch, en dat is net alles. Akrobatyske feats lykas dizze fereaskje it gebrûk fan runtime-refleksje. Sels Rob Pike hâldt net fan yndividuen dy't dit misbrûke, sa't hy neamde yn ien fan syn rapporten.

Dit is in krêftich ark dat mei foarsichtigens brûkt wurde moat. It moat foarkommen wurde, útsein as strikt nedich.

Ik soe nimme D sjabloanen ynstee fan dizze ûnsin. Hoe kin immen dat sizze interface{} lêsberder of sels typefeilich?

De weeën fan ôfhinklikensbehear

Go hat in ynboude ôfhinklikenssysteem boud boppe op populêre hostingproviders VCS. De ark dy't mei Go komme, witte oer dizze tsjinsten en kinne koade fan har downloade, bouwe en ynstallearje yn ien klap. Hoewol dit geweldich is, is d'r in grutte flater mei ferzjeferzje! Ja, it is wier dat jo de boarnekoade kinne krije fan tsjinsten lykas github of bitbucket mei Go-ark, mar jo kinne de ferzje net opjaan. En wer ienfâld ten koste fan nut. Ik kin de logika fan sa'n beslút net begripe.

Nei it stellen fan fragen oer in oplossing foar dit probleem, makke it Go-ûntwikkelteam forum thread, dy't sketste hoe't se om dizze kwestje hinne soene komme. Harren oanbefelling wie om ien dei it hiele repository yn jo projekt te kopiearjen en it "as is" te litten. Wat de hel tinke se? Wy hawwe geweldige ferzjekontrôlesystemen mei geweldige tagging en ferzjestipe dy't de Go-skeppers negearje en gewoan de boarnekoade kopiearje.

Kulturele bagaazje út Xi

Neffens my is Go ûntwikkele troch minsken dy't har hiele libben C hiene brûkt en troch dyjingen dy't net wat nijs woene besykje. De taal kin omskreaun wurde as C mei ekstra tsjillen(orig.: training tsjillen). Der sitte gjin nije ideeën yn, útsein stipe foar parallelisme (dat trouwens prachtich is) en dit is spitich. Jo hawwe poerbêst parallelisme yn in amper brûkbere, lamme taal.

In oar kreakjend probleem is dat Go in prosedueretaal is (lykas de stille horror fan C). Jo einigje mei it skriuwen fan koade yn in prosedurele styl dy't argaysk en ferâldere fielt. Ik wit dat objektrjochte programmearring gjin sulveren kûgel is, mar it soe geweldich wêze om de details yn typen te abstraheren en ynkapseling te leverjen.

Ienfâld foar jo eigen foardiel

Go is ûntwurpen om ienfâldich te wêzen en it slagget op dat doel. It is skreaun foar swakke programmeurs, mei in âlde taal as sjabloan. It komt kompleet mei ienfâldige ark om ienfâldige dingen te dwaan. It is maklik te lêzen en maklik te brûken.

It is ekstreem verbose, net yndrukwekkend en min foar tûke programmeurs.

Спасибо mersinvald foar bewurkings

Boarne: www.habr.com

Add a comment