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

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

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

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

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

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

Мұндағы басты мәселе, біздің бағдарламашыларымыз (шамамен.: Google қызметкерлері) зерттеушілер емес. Олар, әдетте, өте жас, бізге оқудан кейін келеді, мүмкін олар Java немесе C/C++ немесе Python тілін оқыған шығар. Олар керемет тілді түсінбейді, бірақ сонымен бірге біз олардың жақсы бағдарламалық қамтамасыз етуді жасағанын қалаймыз. Сондықтан олардың тілі оларға түсінікті және үйренуге жеңіл болуы керек.
 
Ол таныс болуы керек, шамамен C-ге ұқсас. Google-да жұмыс істейтін бағдарламашылар өз мансабын ерте бастайды және көбінесе процедуралық тілдермен, атап айтқанда C отбасымен таныс. Жаңа бағдарламалау тілінде жылдам өнімділікке қойылатын талап тілдің тым радикалды болмауы керек дегенді білдіреді.

Не? Сонымен, Роб Пайк негізінен Google әзірлеушілері онша жақсы емес екенін айтады, сондықтан олар ақымақтардың тілін жасады (шамамен.: мылқау) бірдеңе істей алатындай етіп. Өз әріптестеріңізге қандай тәкаппар көзқарас? Мен әрқашан Google әзірлеушілері жер бетіндегі ең жарқын және ең жақсы адамдардан таңдалады деп сенетінмін. Әрине, олар қиынырақ нәрсені шеше алады ма?

Шамадан тыс қарапайымдылық артефактілері

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

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

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

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

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

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

Міне, мысалы, сол мәселенің шешімі 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);
    }
}

Ал қазір кім оқуға жарамды? Мен өз дауысымды D-ге беремін. Оның коды әлдеқайда оқылады, себебі ол әрекеттерді анық сипаттайды. D әлдеқайда күрделі ұғымдарды пайдаланады (шамамен.: альтернативті функцияны шақыру и үлгілер) Go үлгісіне қарағанда, бірақ оларды түсінуде күрделі ештеңе жоқ.

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

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

Және бұл мысал қол қойылған түрлер үшін де жұмыс істемейді. Бұл тәсіл өзіңізді қайталамау принципін толығымен бұзады (ҚҰРҒАҚ), ең танымал және айқын қағидалардың бірі, оны елемеу көптеген қателердің көзі болып табылады. Неліктен Го мұны істейді? Бұл тілдің қорқынышты жағы.

D-де бірдей мысал:

import std.stdio;
import std.algorithm;

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

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

Қарапайым типті жүйені айналып өту

Менің ойымша, мұны оқып жатқан Go бағдарламашыларының аузынан көбік шығып, «Сен дұрыс емес істеп жатырсың!» деп айқайлайды. Жалпы функция мен түрлерді жасаудың тағы бір жолы бар, бірақ ол типтік жүйені толығымен бұзады!

Мәселені шешу үшін ақымақ тілді түзетудің мына мысалын қараңыз:

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

Бұл іске асыру Reduce мақаласынан алынған Go тіліндегі идиоматикалық генериктер (шамамен.: Мен аудармасын таба алмадым, осыған көмектессеңіз қуаныштымын). Идиоматикалық болса, мен идиоматикалық емес мысалды көруді жек көремін. Қолданылуы interface{} - фарс, ал тілде бұл тек теруді айналып өту үшін қажет. Бұл бос интерфейс және оның барлық түрлері оны жүзеге асырады, бұл барлығына толық еркіндік береді. Бағдарламалаудың бұл стилі өте жағымсыз және бұл бәрі емес. Осындай акробатикалық ерліктерге орындау уақытының шағылысуын пайдалану қажет. Тіпті Роб Пайк өз есептерінің бірінде айтқанындай, мұны теріс пайдаланатын адамдарды ұнатпайды.

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

Мен бұл бос сөздің орнына D үлгілерін алар едім. Мұны қалай біреу айта алады interface{} оқуға ыңғайлы немесе тіпті қауіпсіз теру?

Тәуелділікті басқарудың қиыншылықтары

Go танымал хостинг провайдерлерінің үстіне салынған бекітілген тәуелділік жүйесі бар VCS. Go қызметімен бірге келетін құралдар бұл қызметтер туралы біледі және олардан кодты бір сәтте жүктеп алуға, құрастыруға және орнатуға болады. Бұл тамаша болғанымен, нұсқада үлкен кемшілік бар! Иә, бастапқы кодты Go құралдарын пайдаланып github немесе bitbucket сияқты қызметтерден алуға болатыны рас, бірақ нұсқаны көрсете алмайсыз. Және тағы да қарапайымдылық пайдалылық есебінен. Мен мұндай шешімнің логикасын түсіне алмаймын.

Осы мәселені шешу туралы сұрақтар қойғаннан кейін Go әзірлеу тобы құрылды форум тақырыбы, олар бұл мәселені қалай шешуге болатынын сипаттады. Олардың ұсынысы бір күні бүкіл репозиторийді жобаңызға көшіріп, оны «сол қалпында» қалдыру болды. Олар не ойлап отыр? Бізде Go жасаушылар елемейтін және бастапқы кодты көшіретін тамаша тегтері мен нұсқа қолдауы бар таңғажайып нұсқаларды басқару жүйелері бар.

Сиден мәдени жүк

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

Тағы бір күрделі мәселе - Go - процедуралық тіл (С дыбыссыз қорқынышты сияқты). Сіз кодты архаикалық және ескірген процедуралық стильде жазасыз. Мен объектіге бағытталған бағдарламалау күміс оқ емес екенін білемін, бірақ мәліметтерді түрлерге түсіріп, инкапсуляцияны қамтамасыз ету тамаша болар еді.

Өз пайдаңыз үшін қарапайымдылық

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

Бұл өте егжей-тегжейлі, әсерсіз және ақылды бағдарламашылар үшін нашар.

сізге рахмет мерсинвальд өңдеулер үшін

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

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