Pam Mae Dylunio yn Ddrwg i Raglenwyr Clyfar

Dros y misoedd diwethaf rydw i wedi bod yn defnyddio Go for implementations. Prawf o Gysyniad (tua.: cod i brofi ymarferoldeb syniad) yn ei amser rhydd, yn rhannol i astudio'r iaith raglennu ei hun. Mae'r rhaglenni eu hunain yn syml iawn ac nid dyna bwrpas yr erthygl hon, ond mae'r profiad o ddefnyddio Go ei hun yn haeddu ychydig eiriau amdano. Ewch yn addo bod (tua.: erthygl a ysgrifennwyd yn 2015) iaith boblogaidd ar gyfer cod graddadwy difrifol. Crëwyd yr iaith gan Google, lle mae'n cael ei defnyddio'n weithredol. Yn y bôn, credaf yn onest fod dyluniad yr iaith Go yn ddrwg i raglenwyr smart.

Wedi'i gynllunio ar gyfer rhaglenwyr gwan?

Mae Go yn hawdd iawn i'w ddysgu, mor hawdd nes i'r cyflwyniad gymryd un noson i mi, ac ar ôl hynny roeddwn i'n gallu codio'n gynhyrchiol yn barod. Enw'r llyfr roeddwn i'n arfer dysgu Go Cyflwyniad i Raglennu ar Waith (cyfieithu), mae ar gael ar-lein. Mae'r llyfr, fel y cod ffynhonnell Go ei hun, yn hawdd i'w ddarllen, mae ganddo enghreifftiau cod da, ac mae'n cynnwys tua 150 o dudalennau y gellir eu darllen mewn un eisteddiad. Mae'r symlrwydd hwn yn adfywiol ar y dechrau, yn enwedig mewn byd rhaglennu sy'n llawn technoleg or-gymhleth. Ond yn y diwedd, yn hwyr neu'n hwyrach mae'r meddwl yn codi: “A yw hyn felly mewn gwirionedd?”

Mae Google yn honni mai symlrwydd Go yw ei bwynt gwerthu ac mae'r iaith wedi'i chynllunio ar gyfer y cynhyrchiant mwyaf mewn timau mawr, ond rwy'n amau ​​hynny. Mae yna nodweddion sydd naill ai ar goll neu'n rhy fanwl. Ac i gyd oherwydd diffyg ymddiriedaeth mewn datblygwyr, gyda'r rhagdybiaeth na allant wneud unrhyw beth yn iawn. Roedd yr awydd hwn am symlrwydd yn benderfyniad ymwybodol gan ddylunwyr yr iaith, ac er mwyn deall yn llawn pam yr oedd ei angen, rhaid inni ddeall cymhelliant y datblygwyr a'r hyn yr oeddent yn ceisio ei gyflawni yn Go.

Felly pam y cafodd ei wneud mor syml? Dyma gwpl o ddyfyniadau Rob Pike (tua.: un o gyd-grewyr yr iaith Go):

Y pwynt allweddol yma yw bod ein rhaglenwyr (tua.: Googlers) nad ydynt yn ymchwilwyr. Maen nhw, fel rheol, yn eithaf ifanc, yn dod atom ar ôl astudio, efallai eu bod wedi astudio Java, neu C/C++, neu Python. Ni allant ddeall iaith wych, ond ar yr un pryd rydym am iddynt greu meddalwedd da. Dyna pam y dylai eu hiaith fod yn hawdd iddynt ei deall a’i dysgu.
 
Dylai fod yn gyfarwydd, yn fras yn debyg i C. Mae rhaglenwyr sy'n gweithio yn Google yn dechrau eu gyrfaoedd yn gynnar ac maent yn gyfarwydd ar y cyfan ag ieithoedd gweithdrefnol, yn enwedig y teulu C. Mae'r gofyniad am gynhyrchiant cyflym mewn iaith raglennu newydd yn golygu na ddylai'r iaith fod yn rhy radical.

Beth? Felly mae Rob Pike yn dweud yn y bôn nad yw datblygwyr Google mor dda â hynny, dyna pam maen nhw wedi creu iaith i idiotiaid (tua.: dumbed down) fel eu bod yn gallu gwneud rhywbeth. Pa fath o olwg drahaus ar eich cydweithwyr eich hun? Rwyf bob amser wedi credu bod datblygwyr Google yn cael eu dewis â llaw o'r disgleiriaf a'r gorau ar y Ddaear. Siawns eu bod yn gallu delio â rhywbeth anoddach?

Arteffactau o symlrwydd gormodol

Mae bod yn syml yn nod teilwng mewn unrhyw ddyluniad, ac mae ceisio gwneud rhywbeth syml yn anodd. Fodd bynnag, wrth geisio datrys (neu hyd yn oed fynegi) problemau cymhleth, weithiau mae angen offeryn cymhleth. Nid cymhlethdod a chymhlethdod yw nodweddion gorau iaith raglennu, ond mae tir canol lle gall yr iaith greu haniaethau cain sy'n hawdd eu deall a'u defnyddio.

Ddim yn fynegiannol iawn

Oherwydd ei ymrwymiad i symlrwydd, nid oes gan Go luniadau sy'n cael eu hystyried yn naturiol mewn ieithoedd eraill. Gall hyn ymddangos yn syniad da ar y dechrau, ond yn ymarferol mae'n arwain at god verbose. Dylai'r rheswm am hyn fod yn amlwg - mae angen iddo fod yn hawdd i ddatblygwyr ddarllen cod pobl eraill, ond mewn gwirionedd mae'r symleiddiadau hyn yn niweidio darllenadwyedd yn unig. Nid oes unrhyw fyrfoddau yn Go: naill ai llawer neu ddim byd.

Er enghraifft, byddai cyfleustodau consol sy'n darllen stdin neu ffeil o ddadleuon llinell orchymyn yn edrych fel hyn:

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

Er bod y cod hwn hefyd yn ceisio bod mor gyffredinol â phosibl, mae geirfa gorfodol Go yn rhwystro, ac o ganlyniad, mae datrys problem syml yn arwain at lawer iawn o god.

Yma, er enghraifft, mae ateb i'r un broblem yn 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);
    }
}

A phwy sy'n fwy darllenadwy nawr? Rhoddaf fy mhleidlais i D. Mae ei god yn llawer mwy darllenadwy oherwydd mae'n disgrifio'r gweithredoedd yn gliriach. Mae D yn defnyddio cysyniadau llawer mwy cymhleth (tua.: galwad swyddogaeth amgen и patrymau) nag yn yr enghraifft Go, ond does dim byd cymhleth mewn gwirionedd ynglŷn â'u deall.

Uffern o gopïo

Awgrym poblogaidd ar gyfer gwella Go yw cyffredinolrwydd. Bydd hyn o leiaf yn helpu i osgoi copïo cod diangen i gefnogi pob math o ddata. Er enghraifft, ni ellir gweithredu swyddogaeth ar gyfer crynhoi rhestr o gyfanrifau mewn unrhyw ffordd arall na thrwy gopïo ei swyddogaeth sylfaenol ar gyfer pob math cyfanrif; nid oes unrhyw ffordd arall:

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

Ac nid yw'r enghraifft hon hyd yn oed yn gweithio ar gyfer mathau wedi'u llofnodi. Mae'r dull hwn yn torri'n llwyr yr egwyddor o beidio ag ailadrodd eich hun (SYCH), un o'r egwyddorion mwyaf enwog ac amlwg, gan anwybyddu pa un yw ffynhonnell llawer o wallau. Pam mae Go yn gwneud hyn? Mae hon yn agwedd ofnadwy ar iaith.

Yr un enghraifft ar D:

import std.stdio;
import std.algorithm;

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

Syml, cain ac yn syth at y pwynt. Y swyddogaeth a ddefnyddir yma yw reduce ar gyfer math o dempled a rhagfynegiad. Ydy, mae hyn eto'n fwy cymhleth na'r fersiwn Go, ond nid yw mor anodd â hynny i raglenwyr craff ei ddeall. Pa enghraifft sy'n haws i'w chynnal ac yn haws ei darllen?

Ffordd osgoi system math syml

Rwy’n dychmygu y bydd rhaglenwyr Go yn darllen hwn yn ewyn yn y geg ac yn sgrechian, “Rydych chi’n gwneud pethau’n anghywir!” Wel, mae yna ffordd arall o wneud swyddogaeth generig a mathau, ond mae'n torri'r system fath yn llwyr!

Edrychwch ar yr enghraifft hon o atgyweiriad iaith wirion i ddatrys y broblem:

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

Mae hyn yn gweithredu Reduce wedi ei fenthyg o'r erthygl Idiomatic generig yn Go (tua.: Ni allwn ddod o hyd i'r cyfieithiad, byddaf yn falch os byddwch chi'n helpu gyda hyn). Wel, os yw'n idiomatig, byddai'n gas gen i weld enghraifft nad yw'n idiomatig. Defnydd interface{} - ffars, ac yn yr iaith dim ond i osgoi teipio sydd ei angen. Mae hwn yn ryngwyneb gwag ac mae pob math yn ei weithredu, gan ganiatáu rhyddid llwyr i bawb. Mae'r arddull hon o raglennu yn ofnadwy o hyll, ac nid dyna'r cyfan. Mae campau acrobatig fel y rhain yn gofyn am ddefnyddio adlewyrchiad amser rhedeg. Nid yw hyd yn oed Rob Pike yn hoffi unigolion sy'n cam-drin hyn, fel y soniodd yn un o'i adroddiadau.

Mae hwn yn offeryn pwerus y dylid ei ddefnyddio gyda gofal. Dylid ei osgoi oni bai ei fod yn gwbl angenrheidiol.

Byddwn i'n cymryd templedi D yn lle'r nonsens hwn. Sut y gall unrhyw un ddweud hynny interface{} yn fwy darllenadwy neu hyd yn oed teipiwch saff?

Gwaeau Rheoli Dibyniaeth

Mae gan Go system dibyniaeth adeiledig ar ben darparwyr cynnal poblogaidd VCS. Mae'r offer sy'n dod gyda Go yn gwybod am y gwasanaethau hyn a gallant lawrlwytho, adeiladu a gosod cod ohonynt mewn un swoop. Er bod hyn yn wych, mae yna ddiffyg mawr gyda fersiynau! Ydy, mae'n wir y gallwch chi gael y cod ffynhonnell o wasanaethau fel github neu bitbucket gan ddefnyddio offer Go, ond ni allwch nodi'r fersiwn. Ac eto symlrwydd ar draul defnyddioldeb. Nid wyf yn gallu deall rhesymeg penderfyniad o'r fath.

Ar ôl gofyn cwestiynau am ateb i'r broblem hon, creodd tîm datblygu Go edefyn fforwm, a oedd yn amlinellu sut yr oeddent yn mynd i fynd o gwmpas y mater hwn. Eu hargymhelliad oedd copïo’r ystorfa gyfan i’ch prosiect un diwrnod a’i gadael “fel y mae.” Beth maen nhw'n ei feddwl? Mae gennym systemau rheoli fersiwn anhygoel gyda thagio gwych a chefnogaeth fersiwn y mae crewyr Go yn eu hanwybyddu a dim ond copïo'r cod ffynhonnell.

Bagiau diwylliannol o Xi

Yn fy marn i, cafodd Go ei ddatblygu gan bobl oedd wedi defnyddio C ar hyd eu hoes a chan y rhai nad oedd eisiau rhoi cynnig ar rywbeth newydd. Gellir disgrifio'r iaith fel C gydag olwynion ychwanegol (orig.: olwynion hyfforddi). Nid oes unrhyw syniadau newydd ynddo, heblaw am gefnogaeth i gyfochrogiaeth (sydd, gyda llaw, yn fendigedig) ac mae hyn yn drueni. Mae gennych chi gyfochrogrwydd rhagorol mewn iaith gloff, prin y gellir ei defnyddio.

Problem fawr arall yw mai iaith drefniadol yw Go (fel arswyd tawel C). Yn y pen draw, rydych chi'n ysgrifennu cod mewn arddull weithdrefnol sy'n teimlo'n hynafol ac yn hen ffasiwn. Gwn nad bwled arian yw rhaglennu gwrthrych-gyfeiriadol, ond byddai'n wych gallu tynnu'r manylion yn fathau a darparu amgáu.

Symlrwydd er eich lles eich hun

Cynlluniwyd Go i fod yn syml ac mae'n llwyddo ar y nod hwnnw. Fe'i hysgrifennwyd ar gyfer rhaglenwyr gwan, gan ddefnyddio hen iaith fel templed. Mae'n dod yn gyflawn gydag offer syml i wneud pethau syml. Mae'n hawdd ei ddarllen ac yn hawdd ei ddefnyddio.

Mae'n hynod ar lafar, yn anargraff, ac yn ddrwg i raglenwyr craff.

Diolch mersinvald am olygiadau

Ffynhonnell: hab.com

Ychwanegu sylw