Неліктен Go ақылсыз бағдарламашылар үшін жаман?

Мақала бұрын жарияланған мақалаға жауап ретінде жазылған антиподеандық мақала.

Неліктен Go ақылсыз бағдарламашылар үшін жаман?

Соңғы екі жылдан астам уақыт ішінде мен дамыған есепшот жүйесі бар мамандандырылған RADIUS серверін енгізу үшін Go қолданбасын қолдандым. Осы жолда мен тілдің қыр-сырын үйреніп жатырмын. Бағдарламалардың өзі өте қарапайым және бұл мақаланың мақсаты емес, бірақ Go пайдалану тәжірибесі оны қорғауда бірнеше сөзге лайық. Go маңызды, масштабталатын код үшін барған сайын негізгі тілге айналуда. Тілді Google жасаған, ол жерде белсенді қолданылады. Қорытындылай келе, мен Go тілінің дизайны UNIntelligent бағдарламашыларына нашар деп ойлаймын.

Әлсіз бағдарламашыларға арналған ба?

Әлсіздер проблемаларды айтады. Идеялар мен армандар туралы күшті әңгіме...

Go үйрену өте оңай, сондықтан кодты іс жүзінде ешқандай жаттығусыз оқи аласыз. Тілдің бұл мүмкіндігі көптеген әлемдік компанияларда кодты негізгі емес мамандармен (менеджерлер, тұтынушылар және т.б.) бірге оқығанда қолданылады. Бұл дизайнға негізделген әзірлеу сияқты әдістемелер үшін өте ыңғайлы.
Тіпті жаңадан бастаған бағдарламашылар бір-екі аптадан кейін өте лайықты кодты шығара бастайды. Мен оқыған кітап «Go Programming» (Марк Саммерфилд бойынша). Кітап өте жақсы, тілдің көптеген нюанстарын қозғайды. Java, PHP сияқты қажетсіз күрделі тілдерден кейін сиқырдың жоқтығы сергітеді. Бірақ ерте ме, кеш пе, көптеген шектеулі бағдарламашылар ескі әдістерді жаңа салада қолдану идеясына ие болады. Бұл шынымен қажет пе?

Роб Пайк (тілдің негізгі идеологы) Go тілін түсінуге оңай және қолдануда тиімді өнеркәсіптік тіл ретінде жасады. Тіл үлкен командалардағы максималды өнімділікке арналған және оған ешқандай күмән жоқ. Көптеген жаңадан келген бағдарламашылар жетіспейтін көптеген мүмкіндіктер бар деп шағымданады. Бұл қарапайымдылыққа деген ұмтылыс тіл дизайнерлерінің саналы шешімі болды және оның не үшін қажет екенін толық түсіну үшін әзірлеушілердің мотивациясын және олар Go бағдарламасында неге қол жеткізгісі келетінін түсінуіміз керек.

Неліктен ол соншалықты қарапайым болды? Міне, Роб Пайктың бірнеше дәйексөздері:

Мұндағы басты мәселе – біздің бағдарламашылар зерттеуші емес. Олар, әдетте, өте жас, бізге оқудан кейін келеді, мүмкін олар Java немесе C/C++ немесе Python тілін оқыған шығар. Олар керемет тілді түсінбейді, бірақ сонымен бірге біз олардың жақсы бағдарламалық қамтамасыз етуді жасағанын қалаймыз. Сондықтан тіл түсінуге, үйренуге жеңіл болуы керек.

Ол таныс болуы керек, шамамен C-ге ұқсас. Google-да жұмыс істейтін бағдарламашылар өз мансабын ерте бастайды және көбінесе процедуралық тілдермен, атап айтқанда C отбасымен таныс. Жаңа бағдарламалау тілінде жылдам өнімділікке қойылатын талап тілдің тым радикалды болмауы керек дегенді білдіреді.

Дана сөздер, солай емес пе?

Қарапайымдылықтың артефактілері

Қарапайымдылық - сұлулықтың қажетті шарты. Лев Толстой.

Оны қарапайым сақтау - кез келген дизайндағы ең маңызды мақсаттардың бірі. Өздеріңіз білетіндей, мінсіз жоба – бұл қосатын ештеңе жоқ жоба емес, одан алып тастайтын ештеңе жоқ жоба. Көптеген адамдар күрделі мәселелерді шешу (немесе тіпті білдіру) үшін күрделі құрал қажет деп санайды. Алайда олай емес. Мысалы, PERL тілін алайық. Тіл идеологтары программист бір мәселені шешудің кем дегенде үш түрлі жолы болуы керек деп есептеді. Го тілінің идеологтары басқа жолды ұстанды; олар мақсатқа жету үшін бір жол, бірақ шын мәнінде жақсы жол жеткілікті деп шешті. Бұл тәсілдің байыпты негізі бар: жалғыз жолды үйрену оңай және ұмыту қиын.

Көптеген мигранттар тілде талғампаз абстракциялардың жоқтығына шағымданады. Иә, бұл дұрыс, бірақ бұл тілдің басты артықшылықтарының бірі. Тілде ең аз сиқыр бар - сондықтан бағдарламаны оқу үшін терең білім қажет емес. Кодтың нақтылығына келетін болсақ, бұл мүлдем проблема емес. Жақсы жазылған Голанг бағдарламасы тігінен оқиды, құрылымы аз немесе мүлдем жоқ. Сонымен қатар, бағдарламаны оқу жылдамдығы, кем дегенде, оны жазу жылдамдығынан үлкен дәрежеде. Егер сіз барлық кодта біркелкі пішімдеу бар деп есептесеңіз (кіріктірілген gofmt пәрмені арқылы орындалады), онда бірнеше қосымша жолдарды оқу мүлде проблема емес.

Өте мәнерлі емес

Өнер еркіндігі шектелген кезде шыдамайды. Дәлдік оның жауапкершілігі емес.

Қарапайымдылыққа ұмтылғандықтан, Go-да басқа тілдерде оларға үйренген адамдар табиғи нәрсе ретінде қабылданатын конструкциялар жоқ. Бастапқыда бұл біршама ыңғайсыз болуы мүмкін, бірақ кейін сіз бағдарламаны оқу әлдеқайда оңай және бір мағыналы екенін байқайсыз.

Мысалы, stdin немесе пәрмен жолы аргументтерінен файлды оқитын консольдік утилита келесідей болады:

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

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

Көшіру тозағы

Адам тозақты өз ішінде алып жүреді. Мартин Лютер.

Жаңадан бастағандар генериктердің болмауына байланысты Go туралы үнемі шағымданады. Бұл мәселені шешу үшін олардың көпшілігі тікелей кодты көшіруді пайдаланады. Мысалы, бүтін сандар тізімін жинақтау функциясы, мұндай кәсіпқойлар бұл функцияны әрбір деректер түрі үшін қарапайым көшіру-қою арқылы жүзеге асыру мүмкін емес деп санайды.

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

Мұндай конструкцияларды жүзеге асыру үшін тілде жеткілікті құралдар бар. Мысалы, жалпы бағдарламалау жақсы болар еді.

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

Біздің код алдыңғы жағдайға қарағанда біршама ұзағырақ болса да, ол жалпыланған. Сондықтан барлық арифметикалық амалдарды орындау бізге қиын болмайды.

Көбісі D тіліндегі бағдарлама айтарлықтай қысқарақ көрінеді және олар дұрыс болады деп айтады.

import std.stdio;
import std.algorithm;

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

Дегенмен, ол қысқарақ, бірақ дұрыс емес, өйткені D іске асыру қателерді өңдеу мәселесін толығымен елемейді.

Шынайы өмірде логиканың күрделілігі артқан сайын алшақтық тез тарылады. Стандартты тіл операторларының көмегімен орындалмайтын әрекетті орындау қажет болғанда, бос орын одан да жылдам жабылады.

Тұрақтылық, кеңейту және оқылу тұрғысынан, менің ойымша, Go тілі көп мағынада жеңілсе де жеңеді.

Жалпыланған бағдарламалау кейбір жағдайларда бізге даусыз артықшылықтар береді. Бұл сұрыптау бумасы арқылы анық көрсетілген. Сонымен, кез келген тізімді сұрыптау үшін бізге 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)
}

Кез келген ашық бастапқы жобаны алып, grep «interface{}» -R пәрменін іске қоссаңыз, шатастыратын интерфейстердің қаншалықты жиі қолданылатынын көресіз. Жақын жолдастар мұның бәрі генериктердің жоқтығынан деп бірден айтады. Дегенмен, бұл әрдайым бола бермейді. Мысал ретінде DELPHI-ді алайық. Осы бірдей генериктердің болуына қарамастан, ол ерікті деректер түрлерімен операциялар үшін арнайы VARIANT түрін қамтиды. Go тілі де солай істейді.

Зеңбіректен торғайларға дейін

Ал күртеше жындының өлшеміне сай болуы керек. Станислав Лек.

Көптеген экстремалды жанкүйерлер Go-да генериктерді жасаудың тағы бір механизмі - рефлексия бар деп мәлімдей алады. Және олар дұрыс болады... бірақ сирек жағдайларда ғана.

Роб Пайк бізге ескертеді:

Бұл сақтықпен қолданылуы керек күшті құрал. Қатты қажет болмаса, одан аулақ болу керек.

Википедия бізге мынаны айтады:

Рефлексия бағдарлама орындалу кезінде өзінің құрылымы мен әрекетін бақылап, өзгерте алатын процесті білдіреді. Рефлексия негізінде жатқан бағдарламалау парадигмасы рефлексиялық бағдарламалау деп аталады. Бұл метабағдарламалаудың бір түрі.

Дегенмен, өзіңіз білетіндей, сіз бәрін төлеуіңіз керек. Бұл жағдайда бұл:

  • бағдарламаларды жазудағы қиындықтар
  • бағдарламаның орындалу жылдамдығы

Сондықтан рефлексияны үлкен калибрлі қару сияқты сақтықпен қолдану керек. Рефлексияны ойланбастан пайдалану оқылмайтын бағдарламаларға, тұрақты қателерге және төмен жылдамдыққа әкеледі. Сноб программисттің басқа, прагматикалық және қарапайым әріптестерінің алдында өз кодын көрсете алуы ғана.

Сиден мәдени жүк? Жоқ, бірнеше тілден!

Байлықпен бірге мұрагерлерге қарыздар да қалдырылады.

Көптеген адамдар бұл тіл толығымен Си мұрасына негізделген деп санайтынына қарамастан, бұл олай емес. Бұл тіл ең жақсы бағдарламалау тілдерінің көптеген аспектілерін қамтиды.

синтаксис

Біріншіден, грамматикалық құрылымдар синтаксисі Си тілінің синтаксисіне негізделеді. Дегенмен, DELPHI тілі де айтарлықтай әсер етті. Осылайша, бағдарламаның оқылуын айтарлықтай төмендететін артық жақшалар толығымен жойылғанын көреміз. Сондай-ақ тілде DELPHI тіліне тән «:=» операторы бар. Пакет ұғымы ADA сияқты тілдерден алынған. Пайдаланылмаған нысандардың декларациясы PROLOG тілінен алынған.

Семантика

Пакеттер DELPHI тілінің семантикасына негізделген. Әрбір бума деректер мен кодты инкапсуляциялайды және жеке және қоғамдық нысандарды қамтиды. Бұл пакет интерфейсін минимумға дейін азайтуға мүмкіндік береді.

Делегация әдісімен жүзеге асыру операциясы DELPHI тілінен алынған.

Жинақ

Әзілдің болуы бекер емес: Go C бағдарламасы құрастырылып жатқанда жасалған. Тілдің күшті жақтарының бірі - оның өте жылдам құрастырылуы. Идея DELPHI тілінен алынған. Әрбір Go бумасы DELPHI модуліне сәйкес келеді. Бұл пакеттер шынымен қажет болғанда ғана қайта құрастырылады. Сондықтан келесі өңдеуден кейін бүкіл бағдарламаны компиляциялаудың қажеті жоқ, тек өзгертілген пакеттер мен осы өзгертілген бумаларға тәуелді бумаларды ғана қайта компиляциялаңыз (және сонда да пакет интерфейстері өзгерген жағдайда ғана).

Жоғары деңгейлі конструкциялар

Бұл тілде C сияқты төмен деңгейлі тілдерге ешқандай қатысы жоқ көптеген жоғары деңгейлі құрылымдар бар.

  • Жолдар
  • Хэш кестелері
  • Кесінділер
  • Үйрекпен теру RUBY сияқты тілдерден алынған (оны, өкінішке орай, көпшілігі түсінбейді немесе оның әлеуетін толық пайдаланбайды).

Жадты басқару

Жадты басқару әдетте жеке мақалаға лайық. Егер C++ сияқты тілдерде басқару толығымен әзірлеушіге жүктелсе, DELPHI сияқты кейінгі тілдерде сілтемелерді санау моделі қолданылды. Бұл тәсілмен циклдік сілтемелерге рұқсат етілмеді, өйткені орфандық кластерлер қалыптасқандықтан, Go жүйесінде мұндай кластерлерді анықтау (C# сияқты) бар. Бұған қоса, қоқыс жинағыш қазіргі уақытта белгілі іске асырулардың көпшілігіне қарағанда тиімдірек және көптеген нақты уақыттағы тапсырмалар үшін пайдаланылуы мүмкін. Тілдің өзі айнымалы мәнді сақтауға арналған стекке бөлуге болатын жағдайларды таниды. Бұл жад менеджеріне жүктемені азайтады және бағдарламаның жылдамдығын арттырады.

Сәйкестік және сәйкестік

Тілдің параллельдігі мен бәсекеге қабілеттілігі мақтауға келмейді. Ешбір төмен деңгейлі тіл тіпті Go-мен қашықтан бәсекелесе алмайды. Әділ болу үшін, модельді тілдің авторлары ойлап таппағанын, тек жақсы ескі ADA тілінен алынғанын атап өткен жөн. Тіл көп ағынды кодқа тән тығырыққа тірелген және жарыс шарттарымен шамалы күрделі мәселелерге ие бола отырып, барлық процессорларды пайдаланып миллиондаған параллель қосылымдарды өңдеуге қабілетті.

Қосымша артықшылықтар

Пайда болса, бәрі жанқияр болады.

Тіл бізге бірқатар сөзсіз артықшылықтар береді:

  • Жобаны құрастырғаннан кейін бір орындалатын файл қолданбаларды орналастыруды айтарлықтай жеңілдетеді.
  • Статикалық теру және түр туралы қорытынды тесттер жазбастан да кодыңыздағы қателердің санын айтарлықтай азайтады. Мен тесттерді мүлде жазбай жасайтын кейбір бағдарламашыларды білемін және олардың кодының сапасы айтарлықтай зардап шекпейді.
  • Өте қарапайым кросс-компиляция және стандартты кітапхананың тамаша портативтілігі, бұл кросс-платформалық қосымшаларды әзірлеуді айтарлықтай жеңілдетеді.
  • RE2 тұрақты өрнектері ағынға қауіпсіз және болжамды орындалу уақытына ие.
  • Көптеген жобаларды үшінші тарап құрылымдарынсыз орындауға мүмкіндік беретін қуатты стандартты кітапхана.
  • Тіл оны қалай шешуге емес, мәселеге назар аудару үшін жеткілікті күшті, бірақ мәселені тиімді шешуге болатындай төмен деңгейлі.
  • Go eco жүйесінде барлық жағдайларға арналған әзірленген құралдар бар: сынақтар, құжаттамалар, пакеттерді басқару, қуатты линтерлер, код генерациясы, жарыс жағдайларының детекторы және т.б.
  • Go 1.11 нұсқасы танымал VCS хостингінің үстіне салынған кірістірілген семантикалық тәуелділікті басқаруды ұсынды. Go экожүйесін құрайтын барлық құралдар осы қызметтерді олардан кодты жүктеп алу, құрастыру және орнату үшін бір уақытта пайдаланады. Және бұл тамаша. 1.11 нұсқасының келуімен пакетті нұсқалау мәселесі де толығымен шешілді.
  • Тілдің негізгі идеясы сиқырды азайту болғандықтан, тіл әзірлеушілерді қателерді нақты өңдеуге ынталандырады. Және бұл дұрыс, өйткені әйтпесе ол қателерді өңдеуді мүлде ұмытады. Тағы бір нәрсе, әзірлеушілердің көпшілігі қателерді өңдеуді әдейі елемейді, оларды өңдеудің орнына қатені жоғары қарай жіберуді қалайды.
  • Тіл классикалық OOP әдістемесін жүзеге асырмайды, өйткені оның таза түрінде Go-да виртуалдылық жоқ. Дегенмен, интерфейстерді пайдалану кезінде бұл проблема емес. OOP болмауы жаңадан бастағандар үшін кіруге кедергіні айтарлықтай азайтады.

Қоғамның пайдасы үшін қарапайымдылық

Күрдеуі оңай, жеңілдету қиын.

Go қарапайым болу үшін жасалған және ол осы мақсатқа жетеді. Ол командалық жұмыстың артықшылықтарын түсінетін және Кәсіпорын деңгейіндегі тілдердің шексіз өзгермелілігінен шаршаған ақылды бағдарламашыларға арналған. Арсеналында синтаксистік құрылымдардың салыстырмалы түрде аз жиынтығы бар, ол іс жүзінде уақыт өте келе өзгермейді, сондықтан әзірлеушілер тілдік инновацияларды шексіз зерттеуге емес, дамуға көп уақыт бөледі.

Компаниялар бірқатар артықшылықтарға да ие болады: кіру кедергісінің төмендігі маманды тез табуға мүмкіндік береді, ал тілдің өзгермейтіндігі 10 жылдан кейін де бір кодты қолдануға мүмкіндік береді.

қорытынды

Мидың үлкен көлемі ешқашан бірде-бір пілді Нобель сыйлығының лауреаты еткен емес.

Жеке эго командалық рухтан басым болатын бағдарламашылар, сондай-ақ академиялық қиындықтар мен шексіз «өзін-өзі жетілдіруді» жақсы көретін теоретиктер үшін тіл шынымен нашар, өйткені бұл жалпы мақсаттағы қолөнер тілі, ол сізге қол жеткізуге мүмкіндік бермейді. өз жұмысыңыздың нәтижесінен эстетикалық ләззат алыңыз және әріптестеріңіздің алдында өзіңізді кәсіби түрде көрсетіңіз (егер біз интеллектті IQ бойынша емес, осы критерийлер бойынша өлшейтін болсақ). Өмірдегі барлық нәрсе сияқты, бұл жеке басымдық мәселесі. Барлық құнды инновациялар сияқты, тіл де жалпыға бірдей теріске шығарудан жаппай қабылдауға дейін ұзақ жолдан өтті. Тіл қарапайымдылығымен тапқыр, ал сіз білетіндей, тапқырдың бәрі қарапайым!

Ақпарат көзі: www.habr.com

пікір қалдыру