Mengapa Go Buruk bagi Pemrogram yang Tidak Cerdas

Artikel ini ditulis sebagai tanggapan terhadap artikel yang diterbitkan sebelumnya artikel antipodean.

Mengapa Go Buruk bagi Pemrogram yang Tidak Cerdas

Selama lebih dari dua tahun terakhir saya telah menggunakan Go untuk mengimplementasikan server RADIUS khusus dengan sistem penagihan yang dikembangkan. Sepanjang perjalanan, saya mempelajari seluk-beluk bahasa itu sendiri. Programnya sendiri sangat sederhana dan bukan merupakan tujuan artikel ini, namun pengalaman menggunakan Go sendiri layak mendapat beberapa kata sebagai pembelaannya. Go menjadi bahasa yang semakin umum untuk kode yang serius dan dapat diskalakan. Bahasa ini dibuat oleh Google, dan digunakan secara aktif. Intinya, sejujurnya menurut saya desain bahasa Go buruk bagi pemrogram yang tidak cerdas.

Dirancang untuk programmer yang lemah?

Yang lemah membicarakan masalah. Pembicaraan yang kuat tentang ide dan impian...

Go sangat mudah dipelajari, sangat mudah sehingga Anda dapat membaca kodenya tanpa perlu pelatihan sama sekali. Fitur bahasa ini digunakan di banyak perusahaan global ketika kode dibaca bersama dengan spesialis non-inti (manajer, pelanggan, dll.). Ini sangat cocok untuk metodologi seperti Design Driven Development.
Bahkan programmer pemula mulai menghasilkan kode yang cukup baik setelah satu atau dua minggu. Buku yang saya pelajari adalah β€œGo Programming” (oleh Mark Summerfield). Bukunya sangat bagus, menyentuh banyak nuansa bahasa. Setelah bahasa rumit yang tidak perlu seperti Java, PHP, kurangnya keajaiban menyegarkan. Namun cepat atau lambat, banyak programmer terbatas yang memiliki ide untuk menggunakan metode lama di bidang baru. Apakah ini benar-benar diperlukan?

Rob Pike (ideolog utama bahasa tersebut) menciptakan bahasa Go sebagai bahasa industri yang mudah dipahami dan efektif digunakan. Bahasa ini dirancang untuk produktivitas maksimum dalam tim besar dan tidak diragukan lagi. Banyak programmer pemula yang mengeluh karena banyak fitur yang hilang. Keinginan akan kesederhanaan ini merupakan keputusan yang disengaja oleh para desainer bahasa tersebut, dan untuk memahami sepenuhnya mengapa hal itu diperlukan, kita harus memahami motivasi para pengembang dan apa yang ingin mereka capai di Go.

Lalu mengapa dibuat sesederhana itu? Berikut beberapa kutipan dari Rob Pike:

Poin kuncinya di sini adalah pemrogram kami bukanlah peneliti. Mereka biasanya masih sangat muda, datang kepada kami setelah belajar, mungkin mereka mempelajari Java, atau C/C++, atau Python. Mereka tidak dapat memahami bahasa yang bagus, namun pada saat yang sama kami ingin mereka membuat perangkat lunak yang bagus. Itu sebabnya bahasanya harus mudah dipahami dan dipelajari.

Dia seharusnya familiar, secara kasar mirip dengan C. Pemrogram yang bekerja di Google memulai karir mereka sejak dini dan sebagian besar akrab dengan bahasa prosedural, khususnya keluarga C. Persyaratan untuk produktivitas yang cepat dalam bahasa pemrograman baru berarti bahasa tersebut tidak boleh terlalu radikal.

Kata-kata bijak bukan?

Artefak Kesederhanaan

Kesederhanaan adalah syarat penting untuk keindahan. Leo Tolstoy.

Menjaga agar tetap sederhana adalah salah satu tujuan terpenting dalam desain apa pun. Seperti yang Anda ketahui, proyek yang sempurna bukanlah proyek yang tidak ada yang perlu ditambahkan, melainkan proyek yang tidak ada yang perlu dihapus. Banyak orang percaya bahwa untuk memecahkan (atau bahkan mengungkapkan) masalah yang kompleks, diperlukan alat yang kompleks. Namun ternyata tidak. Mari kita ambil contoh bahasa PERL. Para ahli ide bahasa percaya bahwa seorang programmer harus memiliki setidaknya tiga cara berbeda untuk menyelesaikan satu masalah. Para ideolog bahasa Go mengambil jalan yang berbeda; mereka memutuskan bahwa satu cara, tetapi cara yang sangat bagus, sudah cukup untuk mencapai tujuan. Pendekatan ini memiliki landasan yang serius: satu-satunya cara adalah lebih mudah dipelajari dan lebih sulit untuk dilupakan.

Banyak migran mengeluh karena bahasanya tidak mengandung abstraksi yang elegan. Ya, itu benar, tetapi ini adalah salah satu keunggulan utama bahasa ini. Bahasanya mengandung sedikit keajaiban - jadi tidak diperlukan pengetahuan mendalam untuk membaca program ini. Mengenai verbositas kode, ini bukan masalah sama sekali. Program Golang yang ditulis dengan baik dapat dibaca secara vertikal, dengan sedikit atau tanpa struktur. Selain itu, kecepatan membaca suatu program setidaknya lebih besar daripada kecepatan menulisnya. Jika Anda menganggap bahwa semua kode memiliki format yang seragam (dilakukan menggunakan perintah gofmt bawaan), maka membaca beberapa baris tambahan tidak menjadi masalah sama sekali.

Tidak terlalu ekspresif

Seni tidak mentolerir ketika kebebasannya dibatasi. Akurasi bukan tanggung jawabnya.

Karena keinginan untuk kesederhanaan, Go tidak memiliki konstruksi yang dalam bahasa lain dianggap sebagai sesuatu yang alami oleh orang yang terbiasa dengannya. Pada awalnya mungkin agak merepotkan, tetapi kemudian Anda menyadari bahwa program ini jauh lebih mudah dan tidak ambigu untuk dibaca.

Misalnya, utilitas konsol yang membaca stdin atau file dari argumen baris perintah akan terlihat seperti ini:

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {

    flag.Parse()

    scanner := newScanner(flag.Args())

    var text string
    for scanner.Scan() {
        text += scanner.Text()
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    fmt.Println(text)
}

func newScanner(flags []string) *bufio.Scanner {
    if len(flags) == 0 {
        return bufio.NewScanner(os.Stdin)
    }

    file, err := os.Open(flags[0])

    if err != nil {
        log.Fatal(err)
    }

    return bufio.NewScanner(file)
}

Solusi untuk masalah yang sama di D, meskipun terlihat lebih pendek, tidak lebih mudah untuk dibaca

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

Sungguh menyalin

Manusia membawa neraka di dalam dirinya. Martin Luther.

Pemula terus-menerus mengeluh tentang Go karena kurangnya obat generik. Untuk mengatasi masalah ini, kebanyakan dari mereka menggunakan penyalinan kode langsung. Misalnya, fungsi untuk menjumlahkan daftar bilangan bulat, calon profesional tersebut percaya bahwa fungsi tersebut tidak dapat diimplementasikan dengan cara lain selain dengan menyalin-menempelkan untuk setiap tipe data.

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 main() {

    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(int32Sum(list32))
    fmt.Println(int64Sum(list64))
}

Bahasa mempunyai sarana yang cukup untuk melaksanakan konstruksi tersebut. Misalnya, pemrograman generik akan baik-baik saja.

package main

import "fmt"

func Eval32(list []int32, fn func(a, b int32)int32) int32 {
    var res int32
    for _, val := range list {
        res = fn(res, val)
    }
    return res
}

func int32Add(a, b int32) int32 {
    return a + b
}

func int32Sub(a, b int32) int32 {
    return a + b
}

func Eval64(list []int64, fn func(a, b int64)int64) int64 {
    var res int64
    for _, val := range list {
        res = fn(res, val)
    }
    return res
}

func int64Add(a, b int64) int64 {
    return a + b
}

func int64Sub(a, b int64) int64 {
    return a - b
}

func main() {

    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(Eval32(list32, int32Add))
    fmt.Println(Eval64(list64, int64Add))
    fmt.Println(Eval64(list64, int64Sub))
}

Dan, meskipun kode kita ternyata lebih panjang dari kasus sebelumnya, kode tersebut telah menjadi umum. Oleh karena itu, tidak akan sulit bagi kita untuk melaksanakan semua operasi aritmatika.

Banyak yang akan mengatakan bahwa program dalam D terlihat jauh lebih pendek, dan mereka benar.

import std.stdio;
import std.algorithm;

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

Namun, ini hanya lebih pendek, tetapi tidak lebih tepat, karena implementasi D sepenuhnya mengabaikan masalah penanganan kesalahan.

Dalam kehidupan nyata, seiring dengan meningkatnya kompleksitas logika, kesenjangan tersebut menyempit dengan cepat. Kesenjangan ini semakin berkurang dengan cepat ketika Anda perlu melakukan tindakan yang tidak dapat dilakukan menggunakan operator bahasa standar.

Dalam hal pemeliharaan, ekstensibilitas, dan keterbacaan, menurut saya, bahasa Go menang, meski kalah dalam verbositas.

Pemrograman umum dalam beberapa kasus memberi kita manfaat yang tidak dapat disangkal. Hal ini diilustrasikan dengan jelas oleh paket sortir. Jadi, untuk mengurutkan daftar apa pun, kita hanya perlu mengimplementasikan antarmuka sort.Interface.

import "sort"

type Names []string

func (ns Names) Len() int {
    return len(ns)
}

func (ns Names) Less(i, j int) bool {
    return ns[i] < ns[j]
}

func (ns Names) Swap(i, j int) {
    ns[i], ns[j] = ns[j], ns[i]
}

func main() {
    names := Names{"London", "Berlin", "Rim"}
    sort.Sort(names)
}

Jika Anda mengambil proyek sumber terbuka dan menjalankan perintah grep β€œinterface{}” -R, Anda akan melihat seberapa sering antarmuka yang membingungkan digunakan. Kawan-kawan yang berpikiran dekat akan langsung mengatakan bahwa semua ini karena kurangnya obat generik. Namun, hal ini tidak selalu terjadi. Mari kita ambil DELPHI sebagai contoh. Meskipun terdapat obat generik yang sama, ia berisi tipe VARIAN khusus untuk operasi dengan tipe data arbitrer. Bahasa Go melakukan hal yang sama.

Dari meriam hingga burung pipit

Dan jaket pengekang itu harus sesuai dengan ukuran kegilaannya. Stanislav Lec.

Banyak penggemar ekstrem mungkin mengklaim bahwa Go memiliki mekanisme lain untuk membuat obat generik - refleksi. Dan mereka benar... tetapi hanya dalam kasus yang jarang terjadi.

Rob Pike memperingatkan kita:

Ini adalah alat yang ampuh yang harus digunakan dengan hati-hati. Hal ini harus dihindari kecuali benar-benar diperlukan.

Wikipedia memberi tahu kita hal berikut:

Refleksi mengacu pada proses di mana suatu program dapat memantau dan memodifikasi struktur dan perilakunya sendiri selama eksekusi. Paradigma pemrograman yang mendasari refleksi disebut pemrograman reflektif. Ini adalah jenis metaprogramming.

Namun, seperti yang Anda tahu, Anda harus membayar semuanya. Dalam hal ini adalah:

  • kesulitan dalam menulis program
  • kecepatan eksekusi program

Oleh karena itu, refleksi harus digunakan dengan hati-hati, seperti senjata kaliber besar. Penggunaan refleksi yang tidak bijaksana menyebabkan program tidak dapat dibaca, kesalahan terus-menerus, dan kecepatan rendah. Hal yang tepat bagi seorang programmer sombong untuk dapat memamerkan kodenya di depan rekan-rekan lain yang lebih pragmatis dan sederhana.

Bagasi budaya dari Xi? Tidak, dari beberapa bahasa!

Selain harta, utang juga diserahkan kepada ahli waris.

Meskipun banyak yang percaya bahwa bahasa tersebut sepenuhnya didasarkan pada warisan C, kenyataannya tidak demikian. Bahasa ini menggabungkan banyak aspek dari bahasa pemrograman terbaik.

sintaksis

Pertama-tama, sintaksis struktur tata bahasa didasarkan pada sintaksis bahasa C. Namun bahasa DELPHI juga mempunyai pengaruh yang signifikan. Jadi, kita melihat bahwa tanda kurung yang berlebihan, yang sangat mengurangi keterbacaan program, telah dihapus seluruhnya. Bahasa ini juga mengandung operator β€œ:=” yang melekat pada bahasa DELPHI. Konsep paket dipinjam dari bahasa seperti ADA. Deklarasi entitas yang tidak digunakan dipinjam dari bahasa PROLOG.

Semantik

Paket-paket tersebut didasarkan pada semantik bahasa DELPHI. Setiap paket merangkum data dan kode serta berisi entitas swasta dan publik. Ini memungkinkan Anda untuk mengurangi antarmuka paket seminimal mungkin.

Operasi implementasi dengan metode delegasi dipinjam dari bahasa DELPHI.

Kompilasi

Bukan tanpa alasan ada lelucon: Go dikembangkan ketika program C sedang dikompilasi. Salah satu kelebihan bahasa ini adalah kompilasinya yang sangat cepat. Idenya dipinjam dari bahasa DELPHI. Setiap paket Go berhubungan dengan modul DELPHI. Paket-paket ini dikompilasi ulang hanya jika benar-benar diperlukan. Oleh karena itu, setelah pengeditan berikutnya, Anda tidak perlu mengkompilasi seluruh program, melainkan mengkompilasi ulang hanya paket yang diubah dan paket yang bergantung pada paket yang diubah tersebut (dan itupun, hanya jika antarmuka paket telah berubah).

Konstruksi tingkat tinggi

Bahasa tersebut berisi banyak konstruksi tingkat tinggi berbeda yang sama sekali tidak terkait dengan bahasa tingkat rendah seperti C.

  • Senar
  • Tabel hash
  • Irisan
  • Pengetikan bebek dipinjam dari bahasa seperti RUBY (yang sayangnya banyak yang tidak memahami atau menggunakannya secara maksimal).

Manajemen memori

Manajemen memori umumnya memerlukan artikel terpisah. Jika dalam bahasa seperti C++, kontrol sepenuhnya diserahkan kepada pengembang, maka dalam bahasa selanjutnya seperti DELPHI, model penghitungan referensi digunakan. Dengan pendekatan ini, referensi siklik tidak diperbolehkan, karena cluster yatim piatu telah terbentuk, maka Go memiliki deteksi bawaan terhadap cluster tersebut (seperti C#). Selain itu, pengumpul sampah lebih efisien daripada kebanyakan implementasi yang dikenal saat ini dan sudah dapat digunakan untuk banyak tugas waktu nyata. Bahasa itu sendiri mengenali situasi ketika nilai untuk menyimpan variabel dapat dialokasikan pada tumpukan. Hal ini mengurangi beban pada manajer memori dan meningkatkan kecepatan program.

Konkurensi dan Konkurensi

Paralelisme dan daya saing bahasa ini sungguh luar biasa. Tidak ada bahasa tingkat rendah yang dapat bersaing dengan Go. Agar adil, perlu dicatat bahwa model tersebut tidak ditemukan oleh penulis bahasa tersebut, tetapi hanya dipinjam dari bahasa ADA lama yang bagus. Bahasa ini mampu memproses jutaan koneksi paralel menggunakan semua CPU, sekaligus memiliki masalah yang lebih mudah dengan kebuntuan dan kondisi balapan yang khas untuk kode multi-utas.

Keuntungan tambahan

Jika menguntungkan, semua orang akan menjadi tidak mementingkan diri sendiri.

Bahasa juga memberi kita sejumlah manfaat yang tidak diragukan lagi:

  • Satu file yang dapat dieksekusi setelah membangun proyek sangat menyederhanakan penerapan aplikasi.
  • Pengetikan statis dan inferensi tipe dapat secara signifikan mengurangi jumlah kesalahan dalam kode Anda, bahkan tanpa tes penulisan. Saya mengenal beberapa pemrogram yang melakukannya tanpa menulis tes sama sekali dan kualitas kode mereka tidak mengalami penurunan yang signifikan.
  • Kompilasi silang yang sangat sederhana dan portabilitas perpustakaan standar yang sangat baik, yang sangat menyederhanakan pengembangan aplikasi lintas platform.
  • Ekspresi reguler RE2 aman untuk thread dan memiliki waktu eksekusi yang dapat diprediksi.
  • Pustaka standar yang kuat yang memungkinkan sebagian besar proyek dilakukan tanpa kerangka kerja pihak ketiga.
  • Bahasanya cukup kuat untuk berfokus pada masalah dibandingkan bagaimana menyelesaikannya, namun tingkatannya cukup rendah sehingga masalah dapat diselesaikan secara efisien.
  • Sistem Go eco sudah berisi alat yang dikembangkan untuk semua kesempatan: pengujian, dokumentasi, manajemen paket, linter yang kuat, pembuatan kode, pendeteksi kondisi balapan, dll.
  • Go versi 1.11 memperkenalkan manajemen ketergantungan semantik bawaan, yang dibangun di atas hosting VCS populer. Semua alat yang membentuk ekosistem Go menggunakan layanan ini untuk mengunduh, membuat, dan menginstal kode dari layanan tersebut dalam satu gerakan. Dan itu bagus. Dengan hadirnya versi 1.11, masalah pembuatan versi paket juga teratasi sepenuhnya.
  • Karena ide inti dari bahasa ini adalah untuk mengurangi keajaiban, bahasa tersebut memberi insentif kepada pengembang untuk melakukan penanganan kesalahan secara eksplisit. Dan ini benar, karena jika tidak, penanganan kesalahan akan dilupakan sama sekali. Hal lainnya adalah sebagian besar pengembang sengaja mengabaikan penanganan kesalahan, lebih memilih meneruskan kesalahan ke atas daripada memprosesnya.
  • Bahasa ini tidak mengimplementasikan metodologi OOP klasik, karena dalam bentuknya yang murni tidak ada virtualitas di Go. Namun, hal ini tidak menjadi masalah saat menggunakan antarmuka. Tidak adanya OOP secara signifikan mengurangi hambatan masuk bagi pemula.

Kesederhanaan untuk kepentingan masyarakat

Mudah untuk diperumit, sulit untuk disederhanakan.

Go dirancang untuk menjadi sederhana dan berhasil mencapai tujuan tersebut. Ini ditulis untuk pemrogram cerdas yang memahami manfaat kerja tim dan bosan dengan variabilitas bahasa tingkat Perusahaan yang tiada habisnya. Memiliki seperangkat struktur sintaksis yang relatif kecil, ia praktis tidak mengalami perubahan seiring waktu, sehingga pengembang memiliki banyak waktu luang untuk pengembangan, dan bukan untuk mempelajari inovasi bahasa tanpa henti.

Perusahaan juga mendapatkan sejumlah keuntungan: hambatan masuk yang rendah memungkinkan mereka menemukan spesialis dengan cepat, dan kekekalan bahasa memungkinkan mereka menggunakan kode yang sama bahkan setelah 10 tahun.

Kesimpulan

Ukuran otak yang besar tidak pernah membuat seekor gajah pun menjadi pemenang Hadiah Nobel.

Bagi para programmer yang ego pribadinya lebih diutamakan daripada semangat tim, serta para ahli teori yang menyukai tantangan akademis dan "peningkatan diri" tanpa akhir, bahasa ini sangat buruk, karena ini adalah bahasa artisanal untuk tujuan umum yang tidak memungkinkan Anda untuk mendapatkannya. kesenangan estetis dari hasil kerja Anda dan tunjukkan diri Anda profesional di depan rekan kerja (asalkan kita mengukur kecerdasan berdasarkan kriteria ini, dan bukan berdasarkan IQ). Seperti semua hal dalam hidup, ini adalah masalah prioritas pribadi. Seperti semua inovasi yang bermanfaat, bahasa ini telah mengalami banyak kemajuan dari penolakan universal hingga penerimaan massal. Bahasanya cerdik dalam kesederhanaannya, dan, seperti yang Anda tahu, segala sesuatu yang cerdik itu sederhana!

Sumber: www.habr.com

Tambah komentar