Nima uchun Go dizayni aqlli dasturchilar uchun yomon

O'tgan oylarda men Go'dan ilovalar uchun foydalandim. Kontseptsiya isboti (taxminan.: g'oyaning funksionalligini tekshirish uchun kod) bo'sh vaqtida, qisman dasturlash tilini o'rganish. Dasturlarning o'zi juda oddiy va bu maqolaning maqsadi emas, lekin Go'dan foydalanish tajribasi bu haqda bir necha so'zlarga loyiqdir. Go bo'lishni va'da qiladi (taxminan.: 2015 yilda yozilgan maqola) jiddiy kengaytiriladigan kod uchun mashhur til. Til Google tomonidan yaratilgan bo'lib, u erda faol foydalaniladi. Xulosa qilib aytganda, men Go tilining dizayni aqlli dasturchilar uchun yomon deb o'ylayman.

Zaif dasturchilar uchun mo'ljallanganmi?

Go o'rganish juda oson, shu qadar osonki, tanishuv meni bir oqshom oldi, shundan so'ng men allaqachon samarali kodlashim mumkin edi. Men Go o'rgangan kitobim deyiladi Go da dasturlashga kirish (tarjima), u onlayn mavjud. Kitob, xuddi Go manba kodi kabi, o'qish oson, yaxshi kod misollariga ega va bir vaqtning o'zida o'qilishi mumkin bo'lgan 150 ga yaqin sahifani o'z ichiga oladi. Bu soddalik, ayniqsa, haddan tashqari murakkab texnologiyalar bilan to'ldirilgan dasturlash dunyosida birinchi navbatda tetiklantiruvchi. Ammo oxir-oqibat, ertami-kechmi shunday fikr paydo bo'ladi: "Bu haqiqatan ham shundaymi?"

Google Go-ning soddaligi uning savdo nuqtasi va til katta jamoalarda maksimal mahsuldorlikka mo'ljallanganligini da'vo qilmoqda, ammo men bunga shubha qilaman. Yo'qolgan yoki haddan tashqari batafsil tavsiflangan xususiyatlar mavjud. Va barchasi ishlab chiquvchilarga ishonch yo'qligi sababli, ular hech narsani to'g'ri qila olmaydilar degan taxmin bilan. Ushbu soddalikka intilish til dizaynerlarining ongli qarori edi va bu nima uchun kerakligini to'liq tushunish uchun biz dasturchilarning motivatsiyasini va ular Go'da nimaga erishmoqchi bo'lganini tushunishimiz kerak.

Xo'sh, nega bu juda oddiy qilingan? Mana bir nechta iqtibos Rob Pike (taxminan.: Go tilining hammualliflaridan biri):

Bu erda asosiy nuqta shundaki, bizning dasturchilarimiz (taxminan.: Google xodimlari) tadqiqotchilar emas. Ular, qoida tariqasida, juda yosh, bizga o'qishdan keyin kelishadi, ehtimol ular Java, C/C++ yoki Python tillarini o'rgangandirlar. Ular ajoyib tilni tushuna olmaydilar, lekin shu bilan birga biz ulardan yaxshi dasturiy ta'minot yaratishlarini xohlaymiz. Shuning uchun ham ularning tili ular tushunishi va o'rganishi uchun qulay bo'lishi kerak.
 
U tanish bo'lishi kerak, taxminan C ga o'xshash. Googleda ishlaydigan dasturchilar o'z kareralarini erta boshlaydilar va asosan protsessual tillarni, xususan, C oilasini yaxshi bilishadi. Yangi dasturlash tilida tez mahsuldorlikka bo'lgan talab bu til juda radikal bo'lmasligi kerakligini anglatadi.

Nima? Shunday qilib, Rob Pike asosan Google ishlab chiquvchilari unchalik yaxshi emasligini, shuning uchun ular ahmoqlar uchun til yaratganligini aytadi (taxminan.: dumbed down) ular nimadir qila olishlari uchun. O'z hamkasblaringizga qanday mag'rur qarash? Men har doim Google ishlab chiquvchilari dunyodagi eng yorqin va eng zo'rlaridan tanlab olinganiga ishonganman. Albatta, ular qiyinroq narsani hal qilishlari mumkinmi?

Haddan tashqari soddalik artefaktlari

Oddiy bo'lish har qanday dizayndagi munosib maqsaddir va oddiy narsani qilishga urinish qiyin. Biroq, murakkab muammolarni hal qilishga (yoki hatto ifodalashga) harakat qilganda, ba'zida murakkab vosita kerak bo'ladi. Murakkablik va murakkablik dasturlash tilining eng yaxshi xususiyatlari emas, lekin til tushunish va ishlatish uchun qulay bo'lgan oqlangan abstraktsiyalarni yaratishi mumkin bo'lgan o'rta zamin mavjud.

Juda ifodali emas

Oddiylikka sodiqligi tufayli Go boshqa tillarda tabiiy deb qabul qilinadigan konstruktsiyalarga ega emas. Avvaliga bu yaxshi fikrdek tuyulishi mumkin, lekin amalda bu batafsil kodga olib keladi. Buning sababi aniq bo'lishi kerak - ishlab chiquvchilar uchun boshqa odamlarning kodini o'qish oson bo'lishi kerak, lekin aslida bu soddalashtirishlar faqat o'qishga zarar keltiradi. Go'da qisqartmalar mavjud emas: ko'p yoki hech narsa.

Misol uchun, stdin yoki buyruq qatori argumentlaridan faylni o'qiydigan konsol yordam dasturi quyidagicha ko'rinadi:

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

Garchi bu kod ham iloji boricha umumiy bo'lishga harakat qilsa-da, Go'ning majburiy so'zlashuvi to'sqinlik qiladi va natijada oddiy muammoni hal qilish katta hajmdagi kodga olib keladi.

Bu erda, masalan, xuddi shu muammoning echimi 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);
    }
}

Va endi kim ko'proq o'qiydi? Men o'z ovozimni D ga beraman. Uning kodi ancha o'qilishi mumkin, chunki u harakatlarni aniqroq tasvirlab beradi. D ancha murakkab tushunchalardan foydalanadi (taxminan.: muqobil funksiya chaqiruvi ΠΈ andozalar) Go misoliga qaraganda, lekin ularni tushunishda aslida hech qanday murakkab narsa yo'q.

Nusxa ko'chirish jahannami

Go-ni yaxshilash bo'yicha mashhur taklif - bu umumiylik. Bu hech bo'lmaganda barcha ma'lumotlar turlarini qo'llab-quvvatlash uchun kodni keraksiz nusxalashdan qochishga yordam beradi. Masalan, butun sonlar ro'yxatini yig'ish funktsiyasini har bir butun son turi uchun uning asosiy funktsiyasidan nusxa ko'chirishdan boshqa yo'l bilan amalga oshirish mumkin emas; boshqa yo'l yo'q:

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

Va bu misol imzolangan turlar uchun ham ishlamaydi. Ushbu yondashuv o'zingizni takrorlamaslik tamoyilini butunlay buzadi (QURUQ), eng mashhur va ravshan tamoyillardan biri bo'lib, unga e'tibor bermaslik ko'plab xatolarning manbai hisoblanadi. Nega Go buni qiladi? Bu tilning dahshatli jihati.

Xuddi shu misol D da:

import std.stdio;
import std.algorithm;

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

Oddiy, oqlangan va to'g'ridan-to'g'ri nuqtaga. Bu erda ishlatiladigan funktsiya reduce shablon turi va predikat uchun. Ha, bu yana Go versiyasiga qaraganda murakkabroq, ammo aqlli dasturchilar uchun tushunish unchalik qiyin emas. Qaysi misolni saqlash va o'qish osonroq?

Oddiy turdagi tizimni aylanib o'tish

Men buni o'qiyotgan Go dasturchilarining og'zidan ko'pik chiqib, "Buni noto'g'ri qilyapsan!" deb qichqiradi deb o'ylayman. Xo'sh, umumiy funktsiya va turlarni yaratishning yana bir usuli bor, lekin u tip tizimini butunlay buzadi!

Muammoni hal qilish uchun ahmoqona tilni tuzatish misolini ko'rib chiqing:

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

Ushbu amalga oshirish Reduce maqoladan olingan Go'dagi idiomatik generiklar (taxminan.: Tarjimasini topa olmadim, yordam bersangiz xursand bo'laman). Xo'sh, agar u idiomatik bo'lsa, men idiomatik bo'lmagan misolni ko'rishni yomon ko'raman. Foydalanish interface{} - fars, va tilda bu faqat yozishni chetlab o'tish uchun kerak. Bu bo'sh interfeys va barcha turlar uni amalga oshiradi va hamma uchun to'liq erkinlik beradi. Dasturlashning bu uslubi juda xunuk va bu hammasi emas. Bu kabi akrobatik harakatlar ish vaqti aks ettirishdan foydalanishni talab qiladi. Hatto Rob Pike ham buni suiiste'mol qiladigan shaxslarni yoqtirmaydi, bu haqda u o'z hisobotlaridan birida aytib o'tgan.

Bu ehtiyotkorlik bilan ishlatilishi kerak bo'lgan kuchli vositadir. Qattiq zarurat bo'lmasa, undan qochish kerak.

Men bu bema'nilik o'rniga D shablonlarini olgan bo'lardim. Qanday qilib kimdir buni aytishi mumkin interface{} o'qilishi mumkinmi yoki hatto xavfsizroqmi?

Qaramlikni boshqarishning musibatlari

Go mashhur xosting provayderlari ustiga qurilgan o'rnatilgan qaramlik tizimiga ega VCS. Go bilan birga kelgan vositalar ushbu xizmatlar haqida bilishadi va ulardan kodni bir zarbada yuklab olishi, yaratishi va oβ€˜rnatishi mumkin. Bu ajoyib bo'lsa-da, versiyada katta kamchilik bor! Ha, siz Go vositalari yordamida github yoki bitbucket kabi xizmatlardan manba kodini olishingiz mumkin, lekin versiyani ko'rsata olmaysiz. Va yana oddiylik foydalilik hisobiga. Men bunday qarorning mantiqini tushunolmayapman.

Ushbu muammoni hal qilish haqida savollar bergandan so'ng, Go ishlab chiqish guruhi yaratildi forum mavzusi, ular bu masalani qanday hal qilishlari haqida gapirib berdi. Ularning tavsiyasi shunchaki butun omborni loyihangizga bir kun nusxa ko'chirish va uni "xuddi shunday" qoldirish edi. Ular nima deb o'ylashadi? Bizda ajoyib teglar va versiyalarni qo'llab-quvvatlaydigan ajoyib versiyalarni boshqarish tizimlari mavjud, ularni Go yaratuvchilari e'tiborsiz qoldirib, faqat manba kodini nusxalashadi.

Xi dan madaniy yuk

Menimcha, Go butun umri davomida C dan foydalangan odamlar va yangi narsalarni sinab ko'rishni istamaganlar tomonidan ishlab chiqilgan. Tilni qo'shimcha g'ildirakli C deb ta'riflash mumkin (asl.: mashq g'ildiraklari). Unda hech qanday yangi g'oyalar yo'q, parallelizmni qo'llab-quvvatlashdan tashqari (bu, aytmoqchi, ajoyib) va bu sharmandalik. Siz zo'rg'a ishlatib bo'lmaydigan, oqsoq tilda ajoyib parallelizmga egasiz.

Yana bir jirkanch muammo shundaki, Go bu protsessual tildir (C ning jimjimador dahshati kabi). Siz kodni protsessual uslubda yozishni tugatasiz, u eski va eskirgan. Men ob'ektga yo'naltirilgan dasturlash kumush o'q emasligini bilaman, lekin tafsilotlarni turlarga ajratib olish va inkapsulyatsiyani ta'minlash juda yaxshi bo'lardi.

O'zingizning manfaatingiz uchun soddalik

Go oddiy bo'lishi uchun yaratilgan va bu maqsadga erishadi. U zaif dasturchilar uchun eski tildan shablon sifatida foydalangan holda yozilgan. Bu oddiy narsalarni qilish uchun oddiy vositalar bilan to'la. O'qish oson va foydalanish oson.

Bu juda batafsil, ta'sirchan va aqlli dasturchilar uchun yomon.

Rahmat mersinvald tahrirlar uchun

Manba: www.habr.com

a Izoh qo'shish