Γιατί το Go Design είναι κακό για έξυπνους προγραμματιστές

Τους τελευταίους μήνες χρησιμοποιούσα το Go για υλοποιήσεις. Απόδειξη Concept (περίπου.: κώδικας για να δοκιμάσει τη λειτουργικότητα μιας ιδέας) στον ελεύθερο χρόνο του, εν μέρει για να μελετήσει την ίδια τη γλώσσα προγραμματισμού. Τα ίδια τα προγράμματα είναι πολύ απλά και δεν είναι ο σκοπός αυτού του άρθρου, αλλά η ίδια η εμπειρία χρήσης του Go αξίζει λίγα λόγια για αυτό. Το Go υπόσχεται να γίνει (περίπου.: άρθρο που γράφτηκε το 2015) μια δημοφιλής γλώσσα για σοβαρό επεκτάσιμο κώδικα. Η γλώσσα δημιουργήθηκε από την Google, όπου χρησιμοποιείται ενεργά. Κατώτατη γραμμή, ειλικρινά πιστεύω ότι ο σχεδιασμός της γλώσσας Go είναι κακός για τους έξυπνους προγραμματιστές.

Σχεδιασμένο για αδύναμους προγραμματιστές;

Το Go είναι πολύ εύκολο να το μάθεις, τόσο εύκολο που η εισαγωγή με πήρε ένα βράδυ, μετά από το οποίο μπορούσα ήδη να κωδικοποιήσω παραγωγικά. Το βιβλίο που συνήθιζα να μάθω Go λέγεται Εισαγωγή στον Προγραμματισμό στο Go (μετάφραση), είναι διαθέσιμο στο διαδίκτυο. Το βιβλίο, όπως και ο ίδιος ο πηγαίος κώδικας Go, διαβάζεται εύκολα, έχει καλά παραδείγματα κώδικα και περιέχει περίπου 150 σελίδες που μπορούν να διαβαστούν με μία κίνηση. Αυτή η απλότητα είναι αναζωογονητική στην αρχή, ειδικά σε έναν κόσμο προγραμματισμού γεμάτο με υπερβολικά περίπλοκη τεχνολογία. Αλλά στο τέλος, αργά ή γρήγορα γεννιέται η σκέψη: «Είναι πράγματι έτσι;»

Η Google ισχυρίζεται ότι η απλότητα του Go είναι το σημείο πώλησης και η γλώσσα έχει σχεδιαστεί για μέγιστη παραγωγικότητα σε μεγάλες ομάδες, αλλά αμφιβάλλω. Υπάρχουν χαρακτηριστικά που είτε λείπουν είτε είναι υπερβολικά λεπτομερή. Και όλα αυτά λόγω έλλειψης εμπιστοσύνης στους προγραμματιστές, με την υπόθεση ότι δεν μπορούν να κάνουν τίποτα σωστά. Αυτή η επιθυμία για απλότητα ήταν μια συνειδητή απόφαση των σχεδιαστών της γλώσσας και για να κατανοήσουμε πλήρως γιατί χρειαζόταν, πρέπει να κατανοήσουμε τα κίνητρα των προγραμματιστών και τι προσπαθούσαν να επιτύχουν στο Go.

Γιατί λοιπόν έγινε τόσο απλό; Εδώ είναι μερικά αποσπάσματα Ρομπ Πάικ (περίπου.: ένας από τους συνδημιουργούς της γλώσσας Go):

Το βασικό σημείο εδώ είναι ότι οι προγραμματιστές μας (περίπου.: Υπάλληλοι της Google) δεν είναι ερευνητές. Είναι, κατά κανόνα, αρκετά νέοι, έρχονται σε εμάς μετά τη μελέτη, ίσως σπούδασαν Java, ή C/C++, ή Python. Δεν μπορούν να καταλάβουν μια εξαιρετική γλώσσα, αλλά ταυτόχρονα θέλουμε να δημιουργήσουν καλό λογισμικό. Γι' αυτό η γλώσσα τους πρέπει να είναι εύκολη στην κατανόηση και την εκμάθηση.
 
Θα πρέπει να είναι εξοικειωμένος, χοντρικά παρόμοιος με τον C. Οι προγραμματιστές που εργάζονται στην Google ξεκινούν τη σταδιοδρομία τους νωρίς και είναι κυρίως εξοικειωμένοι με τις διαδικαστικές γλώσσες, ιδιαίτερα την οικογένεια C. Η απαίτηση για γρήγορη παραγωγικότητα σε μια νέα γλώσσα προγραμματισμού σημαίνει ότι η γλώσσα δεν πρέπει να είναι πολύ ριζοσπαστική.

Τι? Οπότε ο Rob Pike λέει βασικά ότι οι προγραμματιστές της 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))
}

Και αυτό το παράδειγμα δεν λειτουργεί καν για υπογεγραμμένους τύπους. Αυτή η προσέγγιση παραβιάζει εντελώς την αρχή του να μην επαναλαμβάνεσαι (DRY), μια από τις πιο διάσημες και προφανείς αρχές, η αγνόηση της οποίας είναι η πηγή πολλών λαθών. Γιατί το κάνει αυτό η Go; Αυτή είναι μια τρομερή πτυχή της γλώσσας.

Το ίδιο παράδειγμα στο 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{} - μια φάρσα, και στη γλώσσα χρειάζεται μόνο για να παρακάμψετε την πληκτρολόγηση. Αυτή είναι μια κενή διεπαφή και όλοι οι τύποι την εφαρμόζουν, επιτρέποντας πλήρη ελευθερία σε όλους. Αυτό το στυλ προγραμματισμού είναι άσχημο και δεν είναι μόνο αυτό. Ακροβατικά κατορθώματα όπως αυτά απαιτούν τη χρήση ανάκλασης χρόνου εκτέλεσης. Ακόμη και ο Rob Pike δεν συμπαθεί τα άτομα που το κάνουν κατάχρηση, όπως ανέφερε σε ένα από τα ρεπορτάζ του.

Αυτό είναι ένα ισχυρό εργαλείο που πρέπει να χρησιμοποιείται με προσοχή. Θα πρέπει να αποφεύγεται εκτός εάν είναι απολύτως απαραίτητο.

Θα έπαιρνα πρότυπα D αντί για αυτές τις ανοησίες. Πώς μπορεί κανείς να το πει αυτό interface{} πιο ευανάγνωστο ή ακόμα και πληκτρολογήστε ασφαλές;

Τα δεινά της διαχείρισης εξάρτησης

Το Go διαθέτει ένα ενσωματωμένο σύστημα εξάρτησης που βασίζεται σε δημοφιλείς παρόχους φιλοξενίας VCS. Τα εργαλεία που συνοδεύουν το Go γνωρίζουν για αυτές τις υπηρεσίες και μπορούν να πραγματοποιήσουν λήψη, δημιουργία και εγκατάσταση κώδικα από αυτές με μια γρήγορη κίνηση. Αν και αυτό είναι υπέροχο, υπάρχει ένα σημαντικό ελάττωμα με την έκδοση! Ναι, είναι αλήθεια ότι μπορείτε να λάβετε τον πηγαίο κώδικα από υπηρεσίες όπως το github ή το bitbucket χρησιμοποιώντας τα εργαλεία Go, αλλά δεν μπορείτε να καθορίσετε την έκδοση. Και πάλι απλότητα σε βάρος της χρησιμότητας. Δεν μπορώ να καταλάβω τη λογική μιας τέτοιας απόφασης.

Μετά από ερωτήσεις σχετικά με μια λύση σε αυτό το πρόβλημα, η ομάδα ανάπτυξης Go δημιούργησε νήμα του φόρουμ, το οποίο περιέγραψε τον τρόπο με τον οποίο επρόκειτο να ξεπεράσουν αυτό το ζήτημα. Η σύστασή τους ήταν απλώς να αντιγράψετε ολόκληρο το αποθετήριο στο έργο σας μια μέρα και να το αφήσετε «ως έχει». Τι στο διάολο σκέφτονται; Έχουμε εκπληκτικά συστήματα ελέγχου εκδόσεων με εξαιρετική προσθήκη ετικετών και υποστήριξη έκδοσης που οι δημιουργοί Go αγνοούν και απλώς αντιγράφουν τον πηγαίο κώδικα.

Πολιτιστικές αποσκευές από τον Xi

Κατά τη γνώμη μου, το Go αναπτύχθηκε από ανθρώπους που χρησιμοποιούσαν το C σε όλη τους τη ζωή και από εκείνους που δεν ήθελαν να δοκιμάσουν κάτι νέο. Η γλώσσα μπορεί να περιγραφεί ως C με επιπλέον τροχούς (προέλευση.: τροχοί προπόνησης). Δεν υπάρχουν νέες ιδέες σε αυτό, εκτός από την υποστήριξη του παραλληλισμού (που, παρεμπιπτόντως, είναι υπέροχο) και αυτό είναι κρίμα. Έχετε εξαιρετικό παραλληλισμό σε μια ελάχιστα χρησιμοποιήσιμη, κουτσή γλώσσα.

Ένα άλλο πρόβλημα που τρίζει είναι ότι το Go είναι μια διαδικαστική γλώσσα (όπως ο σιωπηλός τρόμος του C). Καταλήγετε να γράφετε κώδικα με ένα διαδικαστικό στυλ που μοιάζει αρχαϊκό και ξεπερασμένο. Γνωρίζω ότι ο αντικειμενοστραφής προγραμματισμός δεν είναι μια ασημένια κουκκίδα, αλλά θα ήταν υπέροχο να μπορούσαμε να αφαιρέσουμε τις λεπτομέρειες σε τύπους και να παρέχουμε ενθυλάκωση.

Απλότητα για δικό σας όφελος

Το Go σχεδιάστηκε για να είναι απλό και πετυχαίνει αυτόν τον στόχο. Γράφτηκε για αδύναμους προγραμματιστές, χρησιμοποιώντας μια παλιά γλώσσα ως πρότυπο. Έρχεται πλήρης με απλά εργαλεία για να κάνετε απλά πράγματα. Είναι εύκολο στην ανάγνωση και εύκολο στη χρήση.

Είναι εξαιρετικά περίπλοκο, μη εντυπωσιακό και κακό για έξυπνους προγραμματιστές.

σας ευχαριστώ Μέρσινβαλντ για επεξεργασίες

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο