Waarom Go sleg is vir onslim programmeerders

Die artikel is geskryf as 'n reaksie op 'n voorheen gepubliseerde antipodiese artikel.

Waarom Go sleg is vir onslim programmeerders

Oor die afgelope twee plus jare het ek Go gebruik om 'n gespesialiseerde RADIUS-bediener met 'n ontwikkelde faktureringstelsel te implementeer. Langs die pad leer ek die ingewikkeldhede van die taal self. Die programme self is baie eenvoudig en is nie die doel van hierdie artikel nie, maar die ervaring van die gebruik van Go self verdien 'n paar woorde ter verdediging daarvan. Go word 'n toenemend hoofstroomtaal vir ernstige, skaalbare kode. Die taal is deur Google geskep, waar dit aktief gebruik word. Bottom line, ek dink eerlikwaar dat die ontwerp van die Go-taal sleg is vir UNintelligente programmeerders.

Ontwerp vir swak programmeerders?

Die swakkes praat van probleme. Die sterk praatjies oor idees en drome...

Go is baie maklik om te leer, so maklik dat jy die kode kan lees met feitlik geen opleiding glad nie. Hierdie kenmerk van die taal word in baie globale maatskappye gebruik wanneer die kode saam met nie-kernspesialiste (bestuurders, kliënte, ens.) gelees word. Dit is baie gerieflik vir metodologieë soos Ontwerpgedrewe Ontwikkeling.
Selfs beginner programmeerders begin om redelik ordentlike kode te produseer na 'n week of twee. Die boek waaruit ek gestudeer het, is "Go Programming" (deur Mark Summerfield). Die boek is baie goed, dit raak baie nuanses van die taal aan. Na onnodig ingewikkelde tale soos Java, PHP, is die gebrek aan magie verfrissend. Maar vroeër of later het baie beperkte programmeerders die idee om ou metodes in 'n nuwe veld te gebruik. Is dit regtig nodig?

Rob Pike (die hoofideoloog van die taal) het die Go-taal geskep as 'n industriële taal wat maklik is om te verstaan ​​en effektief om te gebruik. Die taal is ontwerp vir maksimum produktiwiteit in groot spanne en daar is geen twyfel daaroor nie. Baie beginner programmeerders kla dat daar baie funksies is wat hulle ontbreek. Hierdie begeerte na eenvoud was 'n bewuste besluit deur die taal se ontwerpers, en om ten volle te verstaan ​​waarom dit nodig was, moet ons die motivering van die ontwikkelaars verstaan ​​en wat hulle in Go probeer bereik het.

So hoekom is dit so eenvoudig gemaak? Hier is 'n paar aanhalings van Rob Pike:

Die sleutelpunt hier is dat ons programmeerders nie navorsers is nie. Hulle is, as 'n reël, redelik jonk, kom na ons toe nadat hulle studeer het, miskien het hulle Java, of C/C++, of Python bestudeer. Hulle kan nie 'n wonderlike taal verstaan ​​nie, maar terselfdertyd wil ons hê hulle moet goeie sagteware skep. Daarom moet die taal maklik verstaanbaar en leerbaar wees.

Hy behoort bekend te wees, min of meer soortgelyk aan C. Programmeerders wat by Google werk, begin hul loopbane vroeg en is meestal vertroud met proseduretale, veral die C-familie. Die vereiste vir vinnige produktiwiteit in 'n nuwe programmeertaal beteken dat die taal nie te radikaal moet wees nie.

Wyse woorde, nie waar nie?

Artefakte van eenvoud

Eenvoud is 'n noodsaaklike voorwaarde vir skoonheid. Lev Tolstoi.

Om dit eenvoudig te hou is een van die belangrikste doelwitte in enige ontwerp. Soos u weet, is 'n perfekte projek nie 'n projek waar daar niks is om by te voeg nie, maar een waarvan daar niks is om te verwyder nie. Baie mense glo dat 'n komplekse hulpmiddel nodig is om komplekse probleme op te los (of selfs uiting te gee). Dit is egter nie. Kom ons neem die PERL-taal byvoorbeeld. Taalideoloë het geglo dat 'n programmeerder ten minste drie verskillende maniere moet hê om een ​​probleem op te los. Die ideoloë van die Go-taal het 'n ander pad geloop; hulle het besluit dat een manier, maar 'n baie goeie een, genoeg was om die doel te bereik. Hierdie benadering het 'n ernstige grondslag: die enigste manier is makliker om te leer en moeiliker om te vergeet.

Baie migrante kla dat die taal nie elegante abstraksies bevat nie. Ja, dit is waar, maar dit is een van die belangrikste voordele van die taal. Die taal bevat 'n minimum magie - dus word geen diepgaande kennis vereis om die program te lees nie. Wat die breedsprakigheid van die kode betref, is dit glad nie 'n probleem nie. 'n Goedgeskrewe Golang-program lees vertikaal, met min of geen struktuur. Daarbenewens is die spoed van lees van 'n program ten minste 'n orde van grootte groter as die spoed om dit te skryf. As jy dink dat al die kode eenvormige formatering het (gedoen met die ingeboude gofmt-opdrag), dan is die lees van 'n paar ekstra reëls glad nie 'n probleem nie.

Nie baie ekspressief nie

Kuns duld nie wanneer sy vryheid ingeperk word nie. Akkuraatheid is nie sy verantwoordelikheid nie.

As gevolg van die begeerte na eenvoud, ontbreek Go konstrukte wat in ander tale as iets natuurlik beskou word deur mense wat daaraan gewoond is. Aanvanklik is dit dalk ietwat ongerieflik, maar dan merk jy dat die program baie makliker en meer ondubbelsinnig is om te lees.

Byvoorbeeld, 'n konsolehulpmiddel wat stdin lees of 'n lêer vanaf opdragreëlargumente sal soos volg lyk:

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

Die oplossing vir dieselfde probleem in D, hoewel dit ietwat korter lyk, is nie makliker om te lees nie

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

Hel van kopiëring

Die mens dra die hel in homself. Martin Luther.

Beginners kla voortdurend oor Go in terme van die gebrek aan generiese middels. Om hierdie probleem op te los, gebruik die meeste van hulle direkte kodekopiering. Byvoorbeeld, 'n funksie vir die opsomming van 'n lys van heelgetalle, sulke voornemende professionele persone glo dat die funksionaliteit nie op enige ander manier geïmplementeer kan word as deur eenvoudige kopieer-plak vir elke datatipe nie.

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

Die taal het voldoende middele om sulke konstruksies te implementeer. Byvoorbeeld, generiese programmering sal goed wees.

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

En hoewel ons kode ietwat langer geblyk het te wees as die vorige geval, het dit veralgemeen geraak. Daarom sal dit nie vir ons moeilik wees om alle rekenkundige bewerkings te implementeer nie.

Baie sal sê dat 'n program in D aansienlik korter lyk, en hulle sal reg wees.

import std.stdio;
import std.algorithm;

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

Dit is egter net korter, maar nie meer korrek nie, aangesien die D-implementering die probleem van fouthantering heeltemal ignoreer.

In die werklike lewe, soos die kompleksiteit van logika toeneem, vernou die gaping vinnig. Die gaping sluit selfs vinniger wanneer jy 'n aksie moet uitvoer wat nie met standaardtaaloperateurs uitgevoer kan word nie.

Wat handhaafbaarheid, uitbreidbaarheid en leesbaarheid betref, wen die Go-taal myns insiens, hoewel dit in verbosity verloor.

Algemene programmering gee ons in sommige gevalle onbetwisbare voordele. Dit word duidelik geïllustreer deur die sorteerpakket. Dus, om enige lys te sorteer, moet ons net die sort.Interface-koppelvlak implementeer.

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

As jy enige oopbronprojek neem en die grep “interface{}” -R-opdrag uitvoer, sal jy sien hoe gereeld verwarrende koppelvlakke gebruik word. Gelowige kamerade sal dadelik sê dat dit alles te wyte is aan die gebrek aan generiese middels. Dit is egter nie altyd die geval nie. Kom ons neem DELPHI as 'n voorbeeld. Ten spyte van die teenwoordigheid van dieselfde generika, bevat dit 'n spesiale VARIANT-tipe vir bedrywighede met arbitrêre datatipes. Die Go-taal doen dieselfde.

Van 'n kanon tot mossies

En die dwangbaadjie moet pas by die grootte van die waansin. Stanislav Lec.

Baie ekstreme aanhangers kan beweer dat Go 'n ander meganisme het om generiese produkte te skep - refleksie. En hulle sal reg wees ... maar net in seldsame gevalle.

Rob Pike waarsku ons:

Dit is 'n kragtige instrument wat met omsigtigheid gebruik moet word. Dit moet vermy word tensy dit streng nodig is.

Wikipedia vertel ons die volgende:

Refleksie verwys na die proses waartydens 'n program sy eie struktuur en gedrag tydens uitvoering kan monitor en wysig. Die programmeringsparadigma onderliggend aan refleksie word reflektiewe programmering genoem. Dit is 'n tipe metaprogrammering.

Soos u weet, moet u egter vir alles betaal. In hierdie geval is dit:

  • probleme met die skryf van programme
  • program uitvoering spoed

Daarom moet refleksie met omsigtigheid gebruik word, soos 'n grootkaliber wapen. Ondeurdagte gebruik van refleksie lei tot onleesbare programme, konstante foute en lae spoed. Net die ding vir 'n snob-programmeerder om sy kode voor ander, meer pragmatiese en beskeie kollegas te kan spog.

Kulturele bagasie van Xi? Nee, uit 'n aantal tale!

Saam met die fortuin word ook skuld aan die erfgename gelaat.

Ten spyte van die feit dat baie glo dat die taal geheel en al op die C-erfenis gebaseer is, is dit nie die geval nie. Die taal bevat baie aspekte van die beste programmeertale.

sintaksis

Eerstens is die sintaksis van grammatikale strukture gebaseer op die sintaksis van die C-taal. Die DELPHI-taal het egter ook 'n beduidende invloed gehad. Ons sien dus dat die oortollige hakies, wat die leesbaarheid van die program aansienlik verminder, heeltemal verwyder is. Die taal bevat ook die “:=”-operateur inherent aan die DELPHI-taal. Die konsep van pakkette is geleen uit tale soos ADA. Die verklaring van ongebruikte entiteite is uit die PROLOG-taal geleen.

Semantiek

Die pakkette was gebaseer op die semantiek van die DELPHI-taal. Elke pakket bevat data en kode en bevat private en openbare entiteite. Dit laat jou toe om die pakketkoppelvlak tot 'n minimum te verminder.

Die implementeringsoperasie deur delegering-metode is uit die DELPHI-taal ontleen.

Samestelling

Dit is nie sonder rede dat daar 'n grap is nie: Go is ontwikkel terwyl 'n C-program saamgestel is. Een van die sterk punte van die taal is sy ultravinnige samestelling. Die idee is ontleen aan die DELPHI-taal. Elke Go-pakket stem ooreen met 'n DELPHI-module. Hierdie pakkette word slegs hersaamgestel wanneer dit regtig nodig is. Daarom, na die volgende wysiging, hoef jy nie die hele program saam te stel nie, maar eerder net die veranderde pakkette en pakkette wat van hierdie veranderde pakkette afhanklik is, weer saam te stel (en selfs dan, net as die pakketkoppelvlakke verander het).

Hoëvlak konstrukte

Die taal bevat baie verskillende hoëvlakkonstrukte wat geensins verband hou met laevlaktale soos C.

  • Snare
  • Hash-tabelle
  • Snye
  • Eend-tik word geleen uit tale soos RUBY (wat ongelukkig baie nie verstaan ​​of tot sy volle potensiaal gebruik nie).

Geheuebestuur

Geheuebestuur verdien oor die algemeen 'n aparte artikel. As die beheer in tale soos C++ heeltemal aan die ontwikkelaar oorgelaat word, is 'n verwysingtelmodel in latere tale soos DELPHI gebruik. Met hierdie benadering is sikliese verwysings nie toegelaat nie, aangesien weesgroepe gevorm is, dan het Go ingeboude opsporing van sulke trosse (soos C#). Daarbenewens is die vullisverwyderaar doeltreffender as die meeste tans bekende implementerings en kan dit reeds vir baie intydse take gebruik word. Die taal self herken situasies wanneer 'n waarde om 'n veranderlike te stoor aan die stapel toegeken kan word. Dit verminder die las op die geheuebestuurder en verhoog die spoed van die program.

Sameloop en sameloop

Die parallelisme en mededingendheid van die taal is bo lof. Geen laevlaktaal kan selfs op 'n afstand met Go kompeteer nie. Om eerlik te wees, is dit opmerklik dat die model nie deur die skrywers van die taal uitgevind is nie, maar bloot uit die goeie ou ADA-taal geleen is. Die taal is in staat om miljoene parallelle verbindings te verwerk deur alle SVE's te gebruik, terwyl dit 'n orde van grootte minder komplekse probleme het met dooiepunte en rastoestande wat tipies is vir multi-threaded-kode.

Bykomende voordele

As dit winsgewend is, sal almal onbaatsugtig word.

Taal bied ons ook 'n aantal ongetwyfelde voordele:

  • 'n Enkele uitvoerbare lêer na die bou van die projek vergemaklik die ontplooiing van toepassings aansienlik.
  • Statiese tik en tipe afleiding kan die aantal foute in jou kode aansienlik verminder, selfs sonder om toetse te skryf. Ek ken sommige programmeerders wat glad nie toetse skryf nie en die kwaliteit van hul kode ly nie noemenswaardig nie.
  • Baie eenvoudige kruissamestelling en uitstekende oordraagbaarheid van die standaardbiblioteek, wat die ontwikkeling van kruisplatformtoepassings aansienlik vergemaklik.
  • RE2 gereelde uitdrukkings is draad-veilig en het voorspelbare uitvoering tye.
  • 'n Kragtige standaardbiblioteek waarmee die meeste projekte sonder derdeparty-raamwerke kan klaarkom.
  • Die taal is kragtig genoeg om op die probleem te fokus eerder as hoe om dit op te los, maar tog laagvlak genoeg dat die probleem doeltreffend opgelos kan word.
  • Die Go-ekostelsel bevat reeds ontwikkelde gereedskap uit die boks vir alle geleenthede: toetse, dokumentasie, pakketbestuur, kragtige linters, kodegenerering, rastoestanddetektor, ens.
  • Go-weergawe 1.11 het ingeboude semantiese afhanklikheidsbestuur bekendgestel, gebou op die gewilde VCS-gasheer. Al die nutsmiddels waaruit die Go-ekosisteem bestaan, gebruik hierdie dienste om kode daarvan in een slag af te laai, te bou en te installeer. En dit is wonderlik. Met die koms van weergawe 1.11 is die probleem met pakketweergawe ook heeltemal opgelos.
  • Omdat die kerngedagte van die taal is om magie te verminder, moedig die taal ontwikkelaars aan om fouthantering eksplisiet te doen. En dit is korrek, want anders sal dit eenvoudig heeltemal van fouthantering vergeet. Nog 'n ding is dat die meeste ontwikkelaars doelbewus fouthantering ignoreer, eerder as om dit te verwerk om bloot die fout opwaarts aan te stuur.
  • Die taal implementeer nie die klassieke OOP-metodologie nie, aangesien daar in sy suiwer vorm geen virtualiteit in Go is nie. Dit is egter nie 'n probleem wanneer koppelvlakke gebruik word nie. Die afwesigheid van OOP verminder die versperring vir toegang vir beginners aansienlik.

Eenvoud tot gemeenskapsvoordeel

Dit is maklik om te kompliseer, moeilik om te vereenvoudig.

Go is ontwerp om eenvoudig te wees en dit slaag met daardie doelwit. Dit is geskryf vir slim programmeerders wat die voordele van spanwerk verstaan ​​en moeg is vir die eindelose variasie van ondernemingsvlaktale. Met 'n relatief klein stel sintaktiese strukture in sy arsenaal, is dit feitlik nie onderhewig aan veranderinge met verloop van tyd nie, so ontwikkelaars het baie tyd vrygemaak vir ontwikkeling, en nie om eindeloos taalinnovasies te bestudeer nie.

Maatskappye kry ook 'n aantal voordele: 'n lae toegangsversperring stel hulle in staat om vinnig 'n spesialis te vind, en die onveranderlikheid van die taal stel hulle in staat om dieselfde kode selfs na 10 jaar te gebruik.

Gevolgtrekking

Groot breingrootte het nog nooit van enige olifant 'n Nobelpryswenner gemaak nie.

Vir daardie programmeerders wie se persoonlike ego voorrang geniet bo spangees, sowel as teoretici wat lief is vir akademiese uitdagings en eindelose "selfverbetering", is die taal regtig sleg, aangesien dit 'n algemene kunstaal is wat jou nie toelaat om estetiese plesier uit die resultaat van jou werk en toon jouself professioneel voor kollegas (mits ons intelligensie aan hierdie kriteria meet, en nie volgens IK nie). Soos alles in die lewe, is dit 'n kwessie van persoonlike prioriteite. Soos alle waardevolle innovasies, het die taal reeds 'n lang pad gekom van universele ontkenning tot massa-aanvaarding. Die taal is vernuftig in sy eenvoud, en, soos jy weet, is alles vernuftig eenvoudig!

Bron: will.com

Voeg 'n opmerking