Go Zeki Olmayan Programcılar İçin Neden Kötüdür?

Makale daha önce yayınlanmış bir yazıya yanıt olarak yazılmıştır. antipod makalesi.

Go Zeki Olmayan Programcılar İçin Neden Kötüdür?

Geçtiğimiz iki yılı aşkın süredir Go'yu, gelişmiş bir faturalandırma sistemine sahip özel bir RADIUS sunucusu uygulamak için kullanıyorum. Yol boyunca dilin inceliklerini öğreniyorum. Programların kendisi çok basittir ve bu makalenin amacı değildir, ancak Go'yu kullanma deneyimi, savunması için birkaç kelimeyi hak ediyor. Go, ciddi ve ölçeklenebilir kodlar için giderek yaygınlaşan bir dil haline geliyor. Dil, aktif olarak kullanıldığı Google tarafından oluşturuldu. Sonuç olarak, Go dilinin tasarımının akıllı olmayan programcılar için kötü olduğunu düşünüyorum.

Zayıf programcılar için mi tasarlandınız?

Zayıflar sorunlardan bahseder. Fikirler ve hayaller hakkındaki güçlü konuşma...

Go'yu öğrenmesi çok kolaydır, o kadar kolaydır ki kodu neredeyse hiç eğitim almadan okuyabilirsiniz. Dilin bu özelliği, birçok küresel şirkette, kodun çekirdek olmayan uzmanlarla (yöneticiler, müşteriler vb.) birlikte okunması durumunda kullanılmaktadır. Bu, Tasarım Odaklı Geliştirme gibi metodolojiler için çok uygundur.
Acemi programcılar bile bir veya iki hafta sonra oldukça iyi kodlar üretmeye başlar. Çalıştığım kitap “Go Programming” (Mark Summerfield tarafından). Kitap çok güzel, dilin birçok nüansına değiniyor. Java, PHP gibi gereksiz karmaşık dillerden sonra sihir eksikliği canlandırıcıdır. Ancak er ya da geç, pek çok sınırlı programcının aklına eski yöntemleri yeni bir alanda kullanma fikri gelir. Bu gerçekten gerekli mi?

Rob Pike (dilin ana ideoloğu), Go dilini anlaşılması kolay ve kullanımı etkili bir endüstriyel dil olarak yarattı. Dil, büyük ekiplerde maksimum üretkenlik için tasarlanmıştır ve buna hiç şüphe yoktur. Birçok acemi programcı, eksik oldukları birçok özellik olduğundan şikayetçidir. Bu basitlik arzusu, dil tasarımcılarının bilinçli bir kararıydı ve buna neden ihtiyaç duyulduğunu tam olarak anlamak için geliştiricilerin motivasyonunu ve Go'da neyi başarmaya çalıştıklarını anlamamız gerekiyor.

Peki neden bu kadar basit hale getirildi? İşte Rob Pike'tan birkaç alıntı:

Buradaki kilit nokta programcılarımızın araştırmacı olmamasıdır. Kural olarak oldukça gençler, okuduktan sonra bize geliyorlar, belki de Java, C/C++ veya Python öğrenmişlerdir. Harika bir dili anlayamazlar ama aynı zamanda onlardan iyi bir yazılım yaratmalarını istiyoruz. Bu nedenle dilin anlaşılması ve öğrenilmesi kolay olmalıdır.

Kabaca C'ye benzer şekilde konuşması tanıdık olmalıdır. Google'da çalışan programcılar kariyerlerine erken başlarlar ve çoğunlukla prosedürel dillere, özellikle de C ailesine aşinadırlar. Yeni bir programlama dilinde hızlı üretkenlik gereksinimi, dilin çok radikal olmaması gerektiği anlamına gelir.

Akıllıca sözler değil mi?

Sadeliğin Eserleri

Sadelik güzellik için gerekli bir koşuldur. Lev Tolstoy.

Basit tutmak, herhangi bir tasarımın en önemli hedeflerinden biridir. Bildiğiniz gibi mükemmel bir proje, eklenecek hiçbir şeyin olmadığı değil, çıkarılacak hiçbir şeyin olmadığı bir projedir. Birçok kişi, karmaşık sorunları çözmek (veya hatta ifade etmek) için karmaşık bir araca ihtiyaç olduğuna inanıyor. Ancak öyle değil. Örnek olarak PERL dilini ele alalım. Dil ideologları, bir programcının bir sorunu çözmek için en az üç farklı yola sahip olması gerektiğine inanıyordu. Go dilinin ideologları farklı bir yol izlediler; tek bir yolun ama gerçekten iyi bir yolun hedefe ulaşmak için yeterli olduğuna karar verdiler. Bu yaklaşımın ciddi bir temeli var: Tek yol öğrenmesi daha kolay, unutması ise daha zor.

Pek çok göçmen, dilin zarif soyutlamalar içermediğinden şikayetçi. Evet bu doğru ama dilin temel avantajlarından biri de bu. Dil minimum düzeyde sihir içerir; dolayısıyla programı okumak için derin bir bilgiye gerek yoktur. Kodun ayrıntı düzeyine gelince, bu hiç sorun değil. İyi yazılmış bir Golang programı, çok az yapıyla veya hiç yapı olmadan dikey olarak okur. Ek olarak, bir programı okuma hızı, en azından onu yazma hızından bir kat daha fazladır. Kodun tamamının tek tip biçimlendirmeye sahip olduğunu (yerleşik gofmt komutu kullanılarak yapılır) düşünürseniz, fazladan birkaç satırı okumak hiç sorun olmaz.

Çok etkileyici değil

Sanat, özgürlüğünün kısıtlanmasına tahammül etmez. Doğruluk onun sorumluluğunda değildir.

Sadelik arzusu nedeniyle Go, diğer dillerde alışkın insanlar tarafından doğal bir şey olarak algılanan yapılardan yoksundur. İlk başta biraz rahatsız edici olabilir, ancak daha sonra programın çok daha kolay ve okunmasının daha net olduğunu fark edeceksiniz.

Örneğin, stdin'i veya komut satırı argümanlarından bir dosyayı okuyan bir konsol yardımcı programı şöyle görünecektir:

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

D'deki aynı sorunun çözümü biraz daha kısa görünse de okunması kolay değil

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

Kopyalamanın cehennemi

İnsan cehennemi kendi içinde taşır. Martin Luther.

Yeni başlayanlar, jenerik eksikliği nedeniyle Go'dan sürekli şikayet ediyorlar. Bu sorunu çözmek için çoğu doğrudan kod kopyalamayı kullanıyor. Örneğin, bir tamsayı listesinin toplanmasına yönelik bir işlev; bu tür profesyoneller, işlevselliğin, her veri türü için basit kopyalayıp yapıştırma işleminden başka bir şekilde uygulanamayacağına inanırlar.

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

Dil, bu tür yapıları uygulamak için yeterli araca sahiptir. Örneğin, genel programlama iyi olurdu.

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

Ve kodumuz önceki duruma göre biraz daha uzun olmasına rağmen genelleştirildi. Dolayısıyla tüm aritmetik işlemleri uygulamamız zor olmayacaktır.

Birçoğu D'deki bir programın çok daha kısa göründüğünü söyleyecek ve haklı olacaklar.

import std.stdio;
import std.algorithm;

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

Bununla birlikte, D uygulaması hata işleme sorununu tamamen göz ardı ettiğinden, bu yalnızca daha kısadır ancak daha doğru değildir.

Gerçek hayatta mantığın karmaşıklığı arttıkça aradaki fark hızla daralıyor. Standart dil operatörleri kullanılarak gerçekleştirilemeyen bir eylemi gerçekleştirmeniz gerektiğinde boşluk daha da hızlı kapanır.

Sürdürülebilirlik, genişletilebilirlik ve okunabilirlik açısından bence Go dili kazanır, ancak ayrıntı açısından kaybeder.

Bazı durumlarda genelleştirilmiş programlama bize yadsınamaz faydalar sağlar. Bu, sıralama paketinde açıkça gösterilmektedir. Yani herhangi bir listeyi sıralamak için sort.Interface arayüzünü uygulamamız yeterli.

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

Herhangi bir açık kaynak projeyi alıp grep “interface{}” -R komutunu çalıştırırsanız, kafa karıştırıcı arayüzlerin ne kadar sıklıkla kullanıldığını göreceksiniz. Yakın fikirli yoldaşlar, tüm bunların jenerik eksikliğinden kaynaklandığını hemen söyleyeceklerdir. Ancak bu her zaman böyle değildir. Örnek olarak DELPHI'yi ele alalım. Aynı jeneriklerin varlığına rağmen, isteğe bağlı veri türlerine sahip işlemler için özel bir VARIANT türü içerir. Go dili de aynısını yapar.

Serçelerdeki bir silahtan

Ve deli gömleği deliliğin boyutuna uygun olmalı. Stanislav Lec.

Pek çok aşırı hayran, Go'nun jenerik oluşturmak için başka bir mekanizmaya sahip olduğunu iddia edebilir: yansıma. Ve haklı olacaklar... ama sadece nadir durumlarda.

Rob Pike bizi uyarıyor:

Bu, dikkatli kullanılması gereken güçlü bir araçtır. Kesinlikle gerekli olmadıkça kaçınılmalıdır.

Vikipedi bize şunu söylüyor:

Yansıma, bir programın yürütme sırasında kendi yapısını ve davranışını izleyebildiği ve değiştirebildiği süreci ifade eder. Yansımanın altında yatan programlama paradigmasına yansıtıcı programlama denir. Bu bir tür metaprogramlamadır.

Ancak bildiğiniz gibi her şeyin bedelini ödemek zorundasınız. Bu durumda:

  • program yazmanın zorluğu
  • program yürütme hızı

Bu nedenle yansımanın büyük kalibreli bir silah gibi dikkatli kullanılması gerekir. Yansımanın düşüncesiz kullanımı okunamayan programlara, sürekli hatalara ve düşük hıza yol açar. Züppe bir programcının kodunu diğer, daha pragmatik ve mütevazı meslektaşlarının önünde gösterebilmesi tam da gereken şey.

Xi'den gelen kültürel bagaj mı? Hayır, birçok dilden!

Servetin yanı sıra borçlar da mirasçılara kalıyor.

Pek çok kişinin dilin tamamen C mirasına dayandığına inanmasına rağmen durum böyle değil. Dil, en iyi programlama dillerinin birçok yönünü içerir.

sözdizimi

Öncelikle gramer yapılarının sözdizimi C dilinin sözdizimine dayanmaktadır. Ancak DELPHI dilinin de önemli bir etkisi oldu. Böylece programın okunabilirliğini büyük ölçüde azaltan gereksiz parantezlerin tamamen kaldırıldığını görüyoruz. Dil ayrıca DELPHI diline özgü “:=” operatörünü de içerir. Paket kavramı ADA gibi dillerden ödünç alınmıştır. Kullanılmayan varlıkların bildirimi PROLOG dilinden alınmıştır.

Anlambilim

Paketler DELPHI dilinin anlambilimine dayanıyordu. Her paket verileri ve kodu kapsar ve özel ve kamuya ait varlıkları içerir. Bu, paket arayüzünü minimuma indirmenize olanak tanır.

Delegasyon yöntemiyle uygulama işlemi DELPHI dilinden ödünç alınmıştır.

Derleme

Bir şakanın olması sebepsiz değil: Go, bir C programı derlenirken geliştirildi. Dilin güçlü yönlerinden biri ultra hızlı derlemesidir. Fikir DELPHI dilinden ödünç alındı. Her Go paketi bir DELPHI modülüne karşılık gelir. Bu paketler yalnızca gerçekten gerekli olduğunda yeniden derlenir. Bu nedenle, bir sonraki düzenlemeden sonra, programın tamamını derlemeniz gerekmez; bunun yerine yalnızca değiştirilmiş paketleri ve bu değiştirilmiş paketlere bağlı olan paketleri (ve o zaman bile yalnızca paket arayüzleri değişmişse) yeniden derlemeniz gerekir.

Üst düzey yapılar

Dil, C gibi düşük seviyeli dillerle hiçbir şekilde ilgisi olmayan birçok farklı yüksek seviyeli yapı içerir.

  • Teller
  • Hash tabloları
  • Dilimler
  • Ördek yazımı RUBY gibi dillerden ödünç alınmıştır (maalesef çoğu kişi bunu anlamamakta veya tam potansiyelini kullanmamaktadır).

Bellek yönetimi

Bellek yönetimi genel olarak ayrı bir makaleyi hak ediyor. C++ gibi dillerde kontrol tamamen geliştiriciye bırakılmışsa DELPHI gibi daha sonraki dillerde referans sayma modeli kullanılmaya başlanmıştır. Bu yaklaşımla, yetim kümeler oluştuğundan döngüsel referanslara izin verilmiyordu, o zaman Go'nun bu tür kümeleri yerleşik olarak algılaması vardır (C# gibi). Ayrıca çöp toplayıcı, şu anda bilinen uygulamaların çoğundan daha verimlidir ve birçok gerçek zamanlı görev için halihazırda kullanılabilir. Dilin kendisi, bir değişkeni saklayacak bir değerin yığında tahsis edilebildiği durumları tanır. Bu, bellek yöneticisindeki yükü azaltır ve programın hızını artırır.

Eşzamanlılık ve Eşzamanlılık

Dilin paralelliği ve rekabetçiliği övgüyü aşıyor. Hiçbir düşük seviyeli dil Go ile uzaktan bile rekabet edemez. Adil olmak gerekirse, modelin dilin yazarları tarafından icat edilmediğini, sadece eski güzel ADA dilinden ödünç alındığını belirtmekte fayda var. Dil, tüm CPU'ları kullanarak milyonlarca paralel bağlantıyı işleyebilme kapasitesine sahipken, çok iş parçacıklı kod için tipik olan kilitlenmeler ve yarış koşullarıyla ilgili daha az karmaşık sorunlara sahiptir.

Ek yararlar

Kârlı olursa herkes özverili olur.

Dil ayrıca bize bir dizi şüphesiz fayda sağlar:

  • Proje oluşturulduktan sonra tek bir yürütülebilir dosya, uygulamaların dağıtımını büyük ölçüde basitleştirir.
  • Statik yazma ve tür çıkarımı, testleri yazmanıza gerek kalmadan kodunuzdaki hata sayısını önemli ölçüde azaltabilir. Hiç test yazmadan bunu yapan bazı programcılar tanıyorum ve kodlarının kalitesi önemli ölçüde düşmüyor.
  • Standart kütüphanenin çok basit çapraz derlemesi ve mükemmel taşınabilirliği, platformlar arası uygulamaların geliştirilmesini büyük ölçüde basitleştirir.
  • RE2 normal ifadeleri iş parçacığı açısından güvenlidir ve öngörülebilir yürütme sürelerine sahiptir.
  • Çoğu projenin üçüncü taraf çerçeveleri olmadan çalışmasına olanak tanıyan güçlü bir standart kitaplık.
  • Dil, sorunun nasıl çözüleceğinden çok, ona odaklanacak kadar güçlü, ancak sorunun etkili bir şekilde çözülebilmesini sağlayacak kadar düşük düzeydedir.
  • Go eco sistemi halihazırda tüm durumlar için kullanıma hazır geliştirilmiş araçlar içerir: testler, dokümantasyon, paket yönetimi, güçlü linterler, kod oluşturma, yarış koşulları dedektörü vb.
  • Go sürüm 1.11, popüler VCS barındırma üzerine kurulu yerleşik anlamsal bağımlılık yönetimini tanıttı. Go ekosistemini oluşturan tüm araçlar, tek bir hamlede kod indirmek, oluşturmak ve yüklemek için bu hizmetleri kullanır. Ve bu harika. 1.11 sürümünün gelmesiyle birlikte paket versiyonlama sorunu da tamamen çözüldü.
  • Dilin temel fikri büyüyü azaltmak olduğundan, dil geliştiricileri açıkça hata işlemeye teşvik eder. Ve bu doğrudur, çünkü aksi takdirde hata işlemeyi tamamen unutacaktır. Başka bir şey de çoğu geliştiricinin kasıtlı olarak hata işlemeyi görmezden gelmesi ve bunları işlemek yerine hatayı yukarıya doğru iletmeyi tercih etmesidir.
  • Go'da saf haliyle sanallık bulunmadığından, dil klasik OOP metodolojisini uygulamamaktadır. Ancak arayüzleri kullanırken bu bir sorun değildir. OOP'nin yokluğu, yeni başlayanlar için giriş engelini önemli ölçüde azaltır.

Toplum yararına basitlik

Karmaşıklaştırmak kolaydır, basitleştirmek zordur.

Go basit olacak şekilde tasarlandı ve bu hedefte başarılı oldu. Ekip çalışmasının faydalarını anlayan ve Kurumsal düzeydeki dillerin sonsuz değişkenliğinden bıkmış akıllı programcılar için yazılmıştır. Cephaneliğinde nispeten küçük bir dizi sözdizimsel yapıya sahip olduğundan, pratikte zaman içinde değişikliklere tabi değildir, bu nedenle geliştiricilerin, dil yeniliklerini sonsuz bir şekilde incelemek için değil, geliştirme için çok fazla zamanı vardır.

Şirketler ayrıca bir takım avantajlara da sahip oluyor: Düşük giriş engeli, hızlı bir şekilde uzman bulmalarına olanak tanıyor ve dilin değişmezliği, 10 yıl sonra bile aynı kodu kullanmalarına olanak tanıyor.

Sonuç

Büyük beyin büyüklüğü hiçbir fili Nobel Ödülü sahibi yapmadı.

Kişisel egoları takım ruhundan önce gelen programcılar ve akademik zorluklardan ve sonsuz "kişisel gelişimden" hoşlanan teorisyenler için dil gerçekten kötüdür, çünkü genel amaçlı bir zanaat dilidir ve size bir şeyler öğrenmenize izin vermez. işinizin sonucundan estetik zevk alın ve meslektaşlarınızın önünde kendinizi profesyonel gösterin (zekayı IQ'ya göre değil bu kriterlere göre ölçmemiz koşuluyla). Hayattaki her şey gibi bu da kişisel öncelikler meselesidir. Tüm değerli yenilikler gibi, dil de evrensel inkardan kitlesel kabule doğru çoktan uzun bir yol kat etti. Dil, sadeliği bakımından ustacadır ve bildiğiniz gibi, ustaca olan her şey basittir!

Kaynak: habr.com

Yorum ekle