Яагаад Go дизайн ухаалаг програмистуудад муу байдаг вэ?

Сүүлийн саруудад би Go програмыг хэрэгжүүлэхдээ ашиглаж байна. Үзэл баримтлалын баталгаа (ойролцоогоор.: санааны үйл ажиллагааг шалгах код) чөлөөт цагаараа, хэсэгчлэн програмчлалын хэлийг өөрөө судлах. Хөтөлбөрүүд нь өөрөө маш энгийн бөгөөд энэ нийтлэлийн зорилго биш боловч Go-г ашиглах туршлага өөрөө энэ талаар хэдэн үг хэлэх ёстой. Go байхаа амлаж байна (ойролцоогоор.: 2015 онд бичсэн нийтлэл) ноцтой масштабтай кодын түгээмэл хэл. Энэ хэлийг Google-ээс бүтээсэн бөгөөд үүнийг идэвхтэй ашигладаг. Эцэст нь хэлэхэд, би Go хэлний загвар нь ухаалаг програмистуудад муу гэж би бодож байна.

Сул програмистуудад зориулагдсан уу?

Go сурахад маш хялбар, маш амархан тул танилцуулга надад нэг орой болсон бөгөөд үүний дараа би аль хэдийн үр дүнтэй кодлох боломжтой болсон. Миний Go сурдаг байсан номыг гэдэг Go програмчлалын талаархи танилцуулга (орчуулга), үүнийг онлайнаар авах боломжтой. Уг ном нь Go-ийн эх кодын нэгэн адил уншихад хялбар, сайн кодын жишээнүүдтэй, нэг дор унших боломжтой 150 орчим хуудастай. Энэ энгийн байдал нь эхэндээ сэтгэл сэргээдэг, ялангуяа хэт төвөгтэй технологиор дүүрэн програмчлалын ертөнцөд. Гэвч эцэст нь эрт орой хэзээ нэгэн цагт "Энэ үнэхээр тийм үү?" гэсэн бодол төрдөг.

Google Go-ийн энгийн байдал нь түүний борлуулалтын цэг бөгөөд хэл нь том багуудад хамгийн их бүтээмжтэй байхаар бүтээгдсэн гэж мэдэгддэг ч би үүнд эргэлзэж байна. Алдаатай эсвэл хэт нарийвчилсан шинж чанарууд байдаг. Энэ бүхэн нь хөгжүүлэгчдэд итгэлгүй байдлаас болж, тэд юу ч зөв хийж чадахгүй гэсэн таамаглалтай байдаг. Энгийн байх гэсэн энэхүү хүсэл нь тухайн хэлний зохион бүтээгчдийн ухамсартай шийдвэр байсан бөгөөд энэ нь яагаад хэрэгтэй байгааг бүрэн ойлгохын тулд хөгжүүлэгчдийн хүсэл эрмэлзэл, тэд Go дээр юу хүрэхийг хичээж байсныг ойлгох ёстой.

Тэгвэл яагаад үүнийг ийм энгийн болгосон юм бэ? Энд хэдэн эшлэл байна Роб Пайк (ойролцоогоор.: Go хэлийг бүтээгчдийн нэг):

Энд гол зүйл бол манай програмистууд (ойролцоогоор.: Google-ийн ажилтнууд) судлаач биш. Дүрмээр бол тэд нэлээд залуу хүмүүс, магадгүй тэд Java, эсвэл C/C++, эсвэл Python-д суралцсан байж магадгүй юм. Тэд гайхалтай хэлийг ойлгодоггүй, гэхдээ бид тэднээс сайн програм хангамж бүтээхийг хүсч байна. Тийм ч учраас хэл нь тэдэнд ойлгомжтой, сурахад хялбар байх ёстой.
 
Тэр С-тэй бараг адилхан танил байх ёстой. Google-д ажилладаг программистууд карьераа эрт эхлүүлдэг бөгөөд ихэвчлэн процедурын хэл, ялангуяа C гэр бүлийн талаар сайн мэддэг. Шинэ програмчлалын хэл дээр хурдан бүтээмжтэй байх шаардлага нь хэл нь хэт радикал байх ёсгүй гэсэн үг юм.

Юу? Тиймээс Роб Пайк үндсэндээ Google-ийн хөгжүүлэгчид тийм ч сайн биш, ийм учраас тэд тэнэгүүдэд зориулсан хэл бий болгосон гэж хэлж байна (ойролцоогоор.: dumbed down) ингэснээр тэд ямар нэгэн зүйл хийх боломжтой болно. Хамт ажиллагсаддаа ямар их зантай харцтай байдаг вэ? 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 нь алдартай хостинг үйлчилгээ үзүүлэгчдийн дээр суурилагдсан хараат байдлын системтэй VCS програм. Go-д ирдэг хэрэгслүүд нь эдгээр үйлчилгээний талаар мэддэг бөгөөд тэдгээрээс код татаж авах, бүтээх, суулгах боломжтой. Энэ нь гайхалтай хэдий ч хувилбарын хувьд томоохон дутагдал байна! Тийм ээ, та Go хэрэглүүрийг ашиглан github эсвэл bitbucket зэрэг үйлчилгээнүүдээс эх кодыг авч болох нь үнэн, гэхдээ та хувилбарыг зааж өгөх боломжгүй. Ашигтай байдлын зардлаар дахин энгийн байдал. Ийм шийдвэрийн логикийг би ойлгохгүй байна.

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

Си-ийн соёлын тээш

Миний бодлоор Go-г бүх насаараа С-г ашигласан хүмүүс, шинэ зүйл туршиж үзэхийг хүсдэггүй хүмүүс хөгжүүлсэн. Энэ хэлийг нэмэлт дугуйтай C гэж тодорхойлж болно (ориг.: сургалтын дугуй). Үүнд параллелизмыг дэмжихээс өөр шинэ санаа байхгүй (дашрамд хэлэхэд энэ нь гайхамшигтай) бөгөөд энэ нь ичмээр юм. Та бараг ашиглах боломжгүй, доголон хэлээр маш сайн параллелизмтэй.

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

Өөрийнхөө ашиг тусын тулд энгийн байдал

Go нь энгийн байхаар бүтээгдсэн бөгөөд энэ зорилгодоо хүрч чаддаг. Энэ нь сул програмистуудад зориулж хуучин хэлийг загвар болгон ашигласан. Энэ нь энгийн зүйлсийг хийх энгийн хэрэгслүүдийн хамт ирдэг. Уншихад хялбар, хэрэглэхэд хялбар.

Энэ нь маш дэлгэрэнгүй, сэтгэгдэл төрүүлэхгүй, ухаалаг програмистуудад муу юм.

Спасибо мерсинвалд засварлах зориулалттай

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх