Bakit Masama ang Go Design para sa mga Smart Programmer

Sa nakalipas na mga buwan, ginagamit ko ang Go para sa mga pagpapatupad. Patunay ng konsepto (tinatayang: code upang subukan ang pag-andar ng isang ideya) sa kanyang libreng oras, bahagyang upang pag-aralan ang programming language mismo. Ang mga programa mismo ay napaka-simple at hindi ang layunin ng artikulong ito, ngunit ang karanasan sa paggamit ng Go mismo ay nararapat ng ilang mga salita tungkol dito. Nangako si Go na magiging (tinatayang: artikulong isinulat noong 2015) isang tanyag na wika para sa seryosong scalable code. Ang wika ay nilikha ng Google, kung saan ito ay aktibong ginagamit. Bottom line, sa totoo lang iniisip ko na ang disenyo ng wikang Go ay masama para sa mga matalinong programmer.

Idinisenyo para sa mahinang programmer?

Napakadaling matutunan ng Go, napakadali kaya inabot ako ng pagpapakilala isang gabi, pagkatapos nito ay nakapag-code na ako nang produktibo. Ang aklat na dati kong natutunang Go ay tinatawag Isang Panimula sa Programming sa Go (pagsasalin), ito ay magagamit online. Ang aklat, tulad ng mismong source code ng Go, ay madaling basahin, may magagandang halimbawa ng code, at naglalaman ng humigit-kumulang 150 mga pahina na maaaring basahin nang sabay-sabay. Ang pagiging simple na ito ay nagre-refresh sa simula, lalo na sa isang mundo ng programming na puno ng sobrang kumplikadong teknolohiya. Ngunit sa huli, maaga o huli ay bumangon ang pag-iisip: "Ganoon ba talaga?"

Sinasabi ng Google na ang pagiging simple ni Go ay ang punto ng pagbebenta nito at ang wika ay idinisenyo para sa maximum na produktibo sa malalaking koponan, ngunit nagdududa ako dito. May mga feature na nawawala o sobrang detalyado. At lahat dahil sa kawalan ng tiwala sa mga developer, na may pag-aakalang wala silang magagawa nang tama. Ang pagnanais na ito para sa pagiging simple ay isang mulat na desisyon ng mga taga-disenyo ng wika, at upang lubos na maunawaan kung bakit ito kinakailangan, dapat nating maunawaan ang pagganyak ng mga developer at kung ano ang sinusubukan nilang makamit sa Go.

Kaya bakit ito ginawang napakasimple? Narito ang ilang mga quote Rob Pike (tinatayang: isa sa mga co-creator ng Go language):

Ang pangunahing punto dito ay ang aming mga programmer (tinatayang: Mga Googler) ay hindi mga mananaliksik. Sila ay, bilang isang panuntunan, medyo bata, pumunta sa amin pagkatapos ng pag-aaral, marahil sila ay nag-aral ng Java, o C/C++, o Python. Hindi nila maintindihan ang isang mahusay na wika, ngunit sa parehong oras gusto naming lumikha sila ng mahusay na software. Kaya naman ang kanilang wika ay dapat na madaling maunawaan at matutunan nila.
 
Dapat ay pamilyar siya, halos katulad sa C. Ang mga programmer na nagtatrabaho sa Google ay nagsisimula ng kanilang mga karera nang maaga at kadalasan ay pamilyar sa mga procedural na wika, partikular sa pamilyang C. Ang pangangailangan para sa mabilis na produktibidad sa isang bagong programming language ay nangangahulugan na ang wika ay hindi dapat masyadong radikal.

Ano? Kaya't karaniwang sinasabi ni Rob Pike na ang mga developer sa Google ay hindi ganoon kagaling, kaya naman gumawa sila ng wika para sa mga idiot (tinatayang: dumbed down) para may magawa sila. Anong klaseng arogante ang tingin sa sarili mong mga kasamahan? Palagi akong naniniwala na ang mga developer ng Google ay pinili mula sa pinakamaliwanag at pinakamahusay sa Earth. Tiyak na kakayanin nila ang isang bagay na mas mahirap?

Mga artifact ng labis na pagiging simple

Ang pagiging simple ay isang karapat-dapat na layunin sa anumang disenyo, at ang pagsisikap na gumawa ng isang bagay na simple ay mahirap. Gayunpaman, kapag sinusubukang lutasin (o kahit na ipahayag) ang mga kumplikadong problema, kung minsan ang isang kumplikadong tool ay kinakailangan. Ang pagiging kumplikado at pagkasalimuot ay hindi ang pinakamahusay na mga tampok ng isang programming language, ngunit mayroong gitna kung saan ang wika ay maaaring lumikha ng mga eleganteng abstraction na madaling maunawaan at gamitin.

Hindi masyadong expressive

Dahil sa pangako nito sa pagiging simple, kulang ang Go ng mga konstruksyon na itinuturing na natural sa ibang mga wika. Ito ay maaaring mukhang isang magandang ideya sa simula, ngunit sa pagsasagawa ito ay nagreresulta sa verbose code. Ang dahilan para dito ay dapat na malinaw - kailangang maging madali para sa mga developer na basahin ang code ng ibang tao, ngunit sa katunayan ang mga pagpapasimpleng ito ay nakakapinsala lamang sa pagiging madaling mabasa. Walang mga pagdadaglat sa Go: marami man o wala.

Halimbawa, ang isang console utility na nagbabasa ng stdin o isang file mula sa mga argumento ng command line ay magiging ganito:

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

Bagama't sinusubukan din ng code na ito na maging pangkalahatan hangga't maaari, ang sapilitang verbosity ni Go ay humahadlang, at bilang resulta, ang paglutas ng isang simpleng problema ay nagreresulta sa isang malaking halaga ng code.

Narito, halimbawa, ay isang solusyon sa parehong problema sa 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);
    }
}

At sino ang mas nababasa ngayon? Ibibigay ko ang aking boto kay D. Ang kanyang code ay mas nababasa dahil mas malinaw niyang inilalarawan ang mga aksyon. D ay gumagamit ng mas kumplikadong mga konsepto (tinatayang: alternatibong function na tawag ΠΈ mga template) kaysa sa halimbawa ng Go, ngunit wala talagang kumplikado sa pag-unawa sa kanila.

Impiyerno ng pagkopya

Ang isang tanyag na mungkahi para sa pagpapabuti ng Go ay pangkalahatan. Makakatulong ito kahit papaano na maiwasan ang hindi kinakailangang pagkopya ng code upang suportahan ang lahat ng uri ng data. Halimbawa, ang isang function para sa pagbubuod ng isang listahan ng mga integer ay maaaring ipatupad sa walang ibang paraan kundi sa pamamagitan ng pagkopya-paste ng pangunahing function nito para sa bawat uri ng integer; walang ibang paraan:

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

At ang halimbawang ito ay hindi rin gumagana para sa mga naka-sign na uri. Ang pamamaraang ito ay ganap na lumalabag sa prinsipyo ng hindi pag-uulit sa iyong sarili (DRY), isa sa mga pinakatanyag at halatang prinsipyo, na hindi pinapansin kung alin ang pinagmumulan ng maraming pagkakamali. Bakit ginagawa ito ni Go? Ito ay isang kahila-hilakbot na aspeto ng wika.

Parehong halimbawa sa D:

import std.stdio;
import std.algorithm;

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

Simple, elegante at straight to the point. Ang function na ginamit dito ay reduce para sa uri ng template at panaguri. Oo, muli itong mas kumplikado kaysa sa bersyon ng Go, ngunit hindi ganoon kahirap para sa mga matalinong programmer na maunawaan. Aling halimbawa ang mas madaling mapanatili at mas madaling basahin?

Simpleng uri ng system bypass

Iniisip ko na ang mga programmer ng Go na nagbabasa nito ay bumubula ang bibig at sumisigaw, "Mali ang ginagawa mo!" Well, may isa pang paraan upang makagawa ng isang generic na function at mga uri, ngunit ganap nitong sinisira ang uri ng sistema!

Tingnan ang halimbawang ito ng isang hangal na pag-aayos ng wika upang malutas ang problema:

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

Itong pagpapatupad Reduce ay hiniram mula sa artikulo Idiomatic generics sa Go (tinatayang: Hindi ko mahanap ang pagsasalin, matutuwa ako kung tumulong ka dito). Well, kung ito ay idiomatic, ayaw kong makakita ng hindi idiomatic na halimbawa. Paggamit interface{} - isang komedya, at sa wika ay kailangan lamang upang i-bypass ang pag-type. Ito ay isang walang laman na interface at lahat ng uri ay nagpapatupad nito, na nagbibigay-daan sa kumpletong kalayaan para sa lahat. Ang istilong ito ng programming ay napakapangit, at hindi lang iyon. Ang mga akrobatikong gawaing tulad nito ay nangangailangan ng paggamit ng runtime reflection. Kahit si Rob Pike ay hindi gusto ang mga indibidwal na umaabuso nito, gaya ng binanggit niya sa isa sa kanyang mga ulat.

Ito ay isang makapangyarihang tool na dapat gamitin nang may pag-iingat. Dapat itong iwasan maliban kung mahigpit na kinakailangan.

Kukuha ako ng mga template ng D sa halip na ang kalokohang ito. Paano masasabi ng sinuman iyon interface{} mas nababasa o kahit na mag-type ng ligtas?

Ang Mga Kalungkutan ng Pamamahala ng Dependency

Ang Go ay may built-in na dependency system na binuo sa ibabaw ng mga sikat na hosting provider VCS. Ang mga tool na kasama ng Go ay alam ang tungkol sa mga serbisyong ito at maaaring mag-download, bumuo, at mag-install ng code mula sa mga ito nang isang beses. Bagama't ito ay mahusay, mayroong isang malaking depekto sa bersyon! Oo, totoo na makukuha mo ang source code mula sa mga serbisyo tulad ng github o bitbucket gamit ang Go tool, ngunit hindi mo matukoy ang bersyon. At muli ang pagiging simple sa kapinsalaan ng pagiging kapaki-pakinabang. Hindi ko maintindihan ang lohika ng naturang desisyon.

Pagkatapos magtanong tungkol sa solusyon sa problemang ito, gumawa ang Go development team thread ng forum, na binalangkas kung paano nila malalampasan ang isyung ito. Ang kanilang rekomendasyon ay kopyahin lamang ang buong repositoryo sa iyong proyekto balang araw at iwanan ito "as is." Ano ba ang iniisip nila? Mayroon kaming kamangha-manghang mga system ng pagkontrol ng bersyon na may mahusay na pag-tag at suporta sa bersyon na binabalewala ng mga tagalikha ng Go at kinokopya lamang ang source code.

Mga bagahe ng kultura mula kay Xi

Sa aking opinyon, ang Go ay binuo ng mga taong gumamit ng C sa buong buhay nila at ng mga taong ayaw sumubok ng bago. Ang wika ay maaaring ilarawan bilang C na may mga karagdagang gulong(orig.: mga gulong ng pagsasanay). Walang mga bagong ideya sa loob nito, maliban sa suporta para sa paralelismo (na, sa pamamagitan ng paraan, ay kahanga-hanga) at ito ay isang kahihiyan. Mayroon kang mahusay na paralelismo sa isang halos hindi magagamit, pilay na wika.

Ang isa pang lumalangitngit na problema ay ang Go ay isang procedural language (tulad ng silent horror ng C). Nagtatapos ka sa pagsusulat ng code sa isang istilong pamamaraan na parang lipas na at luma na. Alam kong ang object oriented na programming ay hindi isang pilak na bala, ngunit ito ay magiging mahusay na ma-abstract ang mga detalye sa mga uri at magbigay ng encapsulation.

Ang pagiging simple para sa iyong sariling kapakinabangan

Ang Go ay idinisenyo upang maging simple at nagtagumpay ito sa layuning iyon. Ito ay isinulat para sa mga mahihinang programmer, gamit ang isang lumang wika bilang isang template. Kumpleto ito sa mga simpleng tool para gumawa ng mga simpleng bagay. Ito ay madaling basahin at madaling gamitin.

Ito ay napaka-verbose, hindi kapani-paniwala, at masama para sa mga matalinong programmer.

salamat mersinvald para sa mga pag-edit

Pinagmulan: www.habr.com

Magdagdag ng komento