Эмне үчүн Go дизайн акылдуу программисттер үчүн жаман

Акыркы айларда мен Go программасын ишке ашыруу үчүн колдонуп жатам. Жак- Proof (болжол менен: идеянын функционалдуулугун текшерүү үчүн код) бош убактысында, жарым-жартылай программалоо тилин үйрөнүү. Программалардын өзү абдан жөнөкөй жана бул макаланын максаты эмес, бирок Go колдонуу тажрыйбасы бул жөнүндө бир нече сөзгө татыктуу. Барам деп убада берет (болжол менен: 2015-жылы жазылган макала) олуттуу масштабдалуучу код үчүн популярдуу тил. Тил Google тарабынан түзүлгөн, ал жерде активдүү колдонулат. Жыйынтыктап айтканда, мен Go тилинин дизайны акылдуу программисттер үчүн жаман деп ойлойм.

Алсыз программисттер үчүн иштелип чыккан?

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

Google Go'нун жөнөкөйлүгүн анын сатуу пункту деп ырастайт жана тил чоң командаларда максималдуу өндүрүмдүүлүк үчүн иштелип чыккан, бирок мен ага күмөн санайм. Же жок же өтө майда-чүйдөсүнө чейин өзгөчөлүктөрү бар. Мунун баары иштеп чыгуучуларга ишенимдин жоктугунан, алар эч нерсе кыла албайт деген ой менен. Жөнөкөйлүккө болгон бул каалоо тилдин дизайнерлеринин аң-сезимдүү чечими болгон жана ал эмне үчүн керек экенин толук түшүнүү үчүн биз иштеп чыгуучулардын мотивациясын жана алар Go программасында эмнеге жетишүүгө аракет кылып жатышканын түшүнүшүбүз керек.

Анда эмне үчүн ал ушунчалык жөнөкөй болгон? Бул жерде бир нече цитаталар бар Роб Пайк (болжол менен: Go тилинин биргелешип жаратуучуларынын бири):

Бул жерде негизги нерсе биздин программисттер (болжол менен: Google кызматкерлери) изилдөөчүлөр эмес. Алар, эреже катары, абдан жаш, окугандан кийин бизге келишет, балким алар Java, же C/C++ же Python тилдерин үйрөнүшкөн. Алар улуу тилди түшүнө алышпайт, бирок ошол эле учурда биз алардын жакшы программалык камсыздоону жаратышын каалайбыз. Ошондуктан алардын тили аларга түшүнүүгө жана үйрөнүүгө жеңил болушу керек.
 
Ал тааныш болушу керек, болжол менен айтканда, С. 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 алда канча татаал түшүнүктөрдү колдонот (болжол менен: альтернативдик функцияны чакыруу и шаблондор) 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 популярдуу хостинг провайдерлеринин үстүнө курулган көз карандылык тутуму бар SCV. Go менен келген куралдар бул кызматтар жөнүндө билишет жана алардан кодду бир заматта жүктөп, куруп жана орното алышат. Бул сонун болгону менен, версияда чоң кемчилик бар! Ооба, сиз Go куралдарын колдонуу менен github же bitbucket сыяктуу кызматтардан баштапкы кодду ала турганыңыз чын, бирок версияны көрсөтө албайсыз. Жана дагы жөнөкөйлүк пайдалуулуктун эсебинен. Мен мындай чечимдин логикасын түшүнө албай жатам.

Бул көйгөйдү чечүү боюнча суроолорду бергенден кийин, Go өнүктүрүү командасы түзүлгөн форум темасы, алар бул маселенин айланасында кантип чыга тургандыктарын белгилеген. Алардын сунушу бир күнү бүт репозиторийди проектиңизге көчүрүп алып, аны "кандай болсо, ошондой" калтыруу болду. Алар эмнени ойлоп жатышат? Бизде версияны башкаруунун укмуштуудай системалары бар, алар Go жаратуучулары көңүл бурбай, жөн гана баштапкы кодду көчүрүп алышкан.

Сиден маданий жүк

Менин оюмча, Go өмүр бою Сти колдонгон адамдар жана жаңы нерсени сынап көргүсү келбегендер тарабынан иштелип чыккан. Тилди кошумча дөңгөлөктөрү бар C деп айтууга болот(ориг.: машыгуу дөңгөлөктөрү). Анда параллелизмди колдоодон башка эч кандай жаңы идеялар жок (айтмакчы, бул эң сонун) жана бул уят. Сизде эптеп колдонууга жарамдуу, аксак тилде эң сонун параллелизм бар.

Дагы бир кычыраган көйгөй - бул Go процедуралык тил (Стин үнсүз үрөйү сыяктуу). Сиз архаикалык жана эскирген процедуралык стилде код жазасыз. Объектке багытталган программалоо күмүш ок эмес экенин билем, бирок деталдарды түрлөргө бөлүп, инкапсуляцияны камсыз кылуу сонун болмок.

Жөнөкөйлүк өз кызыкчылыгыңыз үчүн

Go жөнөкөй болуу үчүн иштелип чыккан жана бул максатка жетет. Ал эски тилди шаблон катары колдонуп, алсыз программисттер үчүн жазылган. Бул жөнөкөй нерселерди жасоо үчүн жөнөкөй куралдар менен толук келет. Бул окууга жана колдонууга оңой.

Бул өтө кылдат, таасирдүү эмес жана акылдуу программисттер үчүн жаман.

Спасибо мерсинвалд түзөтүүлөр үчүн

Source: www.habr.com

Комментарий кошуу