1 puan yazan GN⁺ 4 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • 2000'lerden 2010'ların başına kadar statik tiplere olan ilginin azalmasının, ardından 2010'ların orta ve sonlarında yeniden artmasının nedeni statik tip sistemlerinin kalitesindeki iyileşme ile açıklanıyor
  • Dinamik tip sistemleri, değişkenlerin ve alanların durumunu ve içeriğini insanların doğrudan değerlendirmesini gerektirir; bilgisayarın ne yardım ettiği ne de engel olduğu, çıplak elle toprak kazmaya benzetiliyor
  • Erken dönem Java veya C++98 gibi geçmişin statik tip sistemleri, nullable ve non-nullable pointer ayrımında bile yardımcı olamayan ve tip adlarını tekrar tekrar yazdıran kağıt kürek benzetmesiyle anlatılıyor
  • TypeScript, Haskell, MyPy, Swift, Rust gibi modern tip sistemleri; null işleme, sum type·union type ve type inference sayesinde program hatalarını doğrulama ve durumu ifade etme konusunda daha iyi destek sunuyor
  • Metot adı otomatik tamamlama gibi IDE özelliklerinin yaygınlaşmasıyla, statik tip sistemine girilen bilginin hata denetiminin ötesinde üretkenlik avantajına dönüştüğü belirtiliyor

Temel sav

  • Statik tiplerin popülerliği basit bir trend değil; yaygın biçimde kullanılabilir statik tip sistemlerinin kalitesi arttıkça yeniden yükselmiş durumda
  • İyi bir kürek varsa toprağı elle değil kürekle kazmak daha iyidir; ama elde yalnızca kağıttan yapılmış bir kürek varsa, çıplak el daha iyidir benzetmesi kullanılıyor
  • Dinamik tip sistemlerinde, programdaki değişkenlerin ve alanların hangi duruma ve içeriğe sahip olduğunu insanın kendisi düşünmek zorundadır; bilgisayar bu değerlendirmeye yardımcı olmaz
  • Kötü bir statik tip sistemi, yardımcı olmaktan çok yük getirebilir; bu da kağıt kürekle toprak kazmaya benzetiliyor

Geçmiş statik tip sistemlerinin sınırları

  • 90'lar ve 2000'lerin başında yaygın kullanılan erken dönem Java veya C++98'in statik tip sistemleri, nullable pointer ile non-nullable pointer'ı ayırmak gibi basit bir işte bile düzgün yardım edemiyordu
  • Geçmişin statik tip sistemleri, sum type olmadan yalnızca product type bulunan yapılar olarak açıklanıyor
  • Geçmişin statik tip sistemleri, tip adlarının her yerde elle yazılmasına yol açıyordu
  • BufferedReader bufferedReader = new BufferedReader(new FileReader(filename)); gibi kodlar küçük bir felaket olarak niteleniyor

Modern tip sistemlerindeki iyileşmeler

  • TypeScript, Haskell, MyPy, Swift, Rust gibi modern tip sistemleri, nullable tiplerle non-nullable tipleri ayırmanın yollarını sunuyor
  • Haskell için Maybe t, TypeScript için T | null, Swift için T?, Rust için Optional<T> örnekleri verilerek, tip sisteminin null denetiminin gerekli olduğu yerleri ve eksik bırakılıp bırakılmadığını gösterebildiği anlatılıyor
  • Bunun sonucunda çalışma zamanında null pointer hatalarının neredeyse hiç görülmediği belirtiliyor
  • Modern tip sistemleri, sum type ya da union type'tan en az birini sunuyor ve "Make invalid states unrepresentable" yaklaşımını uygulamayı mümkün kılıyor
  • Bu yaklaşım, durum makinelerini temsil eden nesnelerde her alanın yalnızca ilgili durumda var olacak şekilde ifade edilmesini sağlıyor
  • Modern tip sistemleri type inference sunuyor; derleyici let x = 5; ifadesini sayı olarak anlayabiliyorsa let x: number = 5; yazmaya gerek kalmıyor

IDE özellikleri ve sonuç

  • Metot adı otomatik tamamlama gibi IDE özellikleri yaygınlaştıkça statik tip sistemlerinin faydası daha da arttı
  • 90'larda Intellisense, Visual Studio'nun temel özelliklerinden biriydi; 2020'lerde ise benzer işlevler neredeyse tüm IDE'lerde ve editörlerde sunuluyor
  • Statik tip sistemine girilen bilgi, yalnızca program hata denetimine değil, ek üretkenlik avantajlarına da yol açıyor
  • İyi bir dinamik tip sistemi, kötü bir statik tip sisteminden daha iyidir; ancak bugün geçmişe kıyasla çok daha iyi statik tip sistemleri kullanabiliyoruz

1 yorum

 
GN⁺ 4 시간 전
Lobste.rs görüşleri
  • Bu yazı iyi ama tamamen katılmıyorum. 2000'lerin başındaki statik tip sistemleri harika olmasa da, hiç statik tip olmamasından çok daha iyiydi diye düşünüyorum
    Kapalı toplam tipler yoktu ama alt tipleme ile bunun önemli bir kısmı modellenebiliyordu; null olmayan tipler yoktu ama C++ referansları ve pointer olmayan tipler, ayrıca Java'nın ilkel tipleri bunun bir kısmını üstleniyordu. Ruby ya da JavaScript'te ise tüm tipler yalnızca null olabilir olmakla kalmıyor, aynı zamanda string gibi de, integer gibi de, programdaki diğer tüm tipler gibi de ele alınabiliyordu; bu daha kötü bir durumdu
    Statik tiplere yönelik akımın değişmesinin büyük bir nedeni, Web 2.0 sosyal ağ patlaması sırasında ilk davranan avantajının her şeyden önemli olmasıydı diye düşünüyorum. Ruby ya da Python ile teknik borç biriktirseniz bile hızlı çıkış yapıp iterasyon yapmak, Friendster ya da Digg gibi geride kalmaktan daha iyiydi; yavaşsanız da o dönemde kolay bulunan düşük faizli parayla daha fazla sunucu satın alabiliyordunuz
    Sonraki mobil patlamada ise yazılım, kontrol edemediğiniz kısıtlı kullanıcı cihazlarında çalışmaya başladı; yavaş dinamik tipli uygulamalar gerçekten yavaştı ve tip hatası olduğunda da sunucudaki gibi en üst düzey yanıt işleyicisiyle zarifçe toparlamak mümkün değildi. O ortamda statik tiplerin güvenliği ve performansı çok daha ikna edici hale geldi

    • Java ve 90'lar usulü C++'ı dinamik tipli dil kod tabanlarıyla karşılaştırıp hata oranlarının benzer olduğunu söyleyen epey makale var ve dinamik dil taraftarları bunu sık sık statik tiplerin faydalı olmadığına kanıt olarak öne sürüyor
      2000'lerin başında ben de katılıyordum, çünkü o dönemin tip sistemleri çoğu zaman neredeyse hiç yanlış çıkmayan özellikleri zorunlu kılarken kodu yapılandırmaya yardımcı olmayan kısıtlar dayatıyordu. Özellikle alt tipleme ile uygulama kalıtımının birleştiği biçim esnek değildi
      Daha modern tip sistemleri kullandıktan sonra fikrim değişti. snmalloc'ta C++ tip sistemiyle bellek sahipliği durum makinesi zorlanıyor; başka kod tabanlarında ise ring buffer sayaçlarının doğru overflow davranışı denetleniyor. İkisi de yanlış olduğunda debug etmesi uğraştırıcı ve yaygın hata kaynakları; derleyicinin, gerçekten doğru sandığım kodda derlemeyi reddedip hatanın ana dala girmesini engellediği durumlar oldu
    • Dinamik tipli dillerle geliştirmenin statik tipli dillere göre daha yavaş olduğunu düşünüyorum. Sürekli tersini iddia edenleri görüyorum ama anlayamıyorum
      IDE'de . tuşuna basıp metod adını biraz yazdıktan sonra doğru adayda Enter'a basmak, her birkaç saniyede bir 2 saniye kazandırıyor; hangi metodların olduğunu bilmediğinizde sınıf tanımını aramak için harcanan 30 saniyeyi de kurtarıyor. Bu ilke https://grugbrain.dev/#grug-on-type-systems içinde de iyi anlatılmış
      Bir fonksiyonun parametre tiplerini yazmaktan çok, metod çağıran kod satırları yazıyoruz; bu yüzden takas dinamik tipler aleyhine ezici biçimde kötü. Değerli olan şey, çalışma anında patlayacak saçma kodlara izin vermek değil, yerel değişken tiplerini atlayabilmekti; statik tipli dillerin bunu en başta yasaklaması gerekmiyordu
    • 2000'lerin başındaki popüler tip sistemleri sadece “muhteşem değildi” düzeyinde değil, gerçekten kötüydü ve aşırı ayrıntılıydı
      Tip sistemini ciddiye alan ender kod tabanlarında, hiçbir şey söylemeyen kodlar sayfalarca birikiyordu; buna rağmen çalışma zamanı koşulları dağ gibi kalıyordu ve Java'da tip hiyerarşisi büyüdükçe program da fiilen yavaşlıyordu. Kod tabanlarının çoğu tipleri seyrek kullanırken bol miktarda çalışma zamanı koşulu ekliyordu; ihtiyaç duyulan test kapsamı açısından dinamik tip sistemlerine kıyasla büyük bir tasarruf da sağlanmıyordu
      Dinamik tipli diller statik ödüller sunmuyordu ama kısa ve özdü; okumak, gözden geçirmek ve test etmek daha kolaydı. Özellikle 90'ların sonu ile 2000'lerin başındaki bağımlılık enjeksiyonu framework'leri gibi, her yeni servis eklediğinizde birden fazla XML dosyasını düzenlemek zorunda kaldığınız ortamlarda bu daha da belirgindi. RAM'in yarısını yiyen bir IDE olmadan da çalışabiliyordunuz
      Kariyerimin ilk dönemi tam olarak böyle geçtiği için yazıya tamamen katılıyorum. Java 1.4'ten Java 6'ya kadar maliyet/fayda oranı o kadar kötüydü ki statik tipli dillerden neredeyse vazgeçmeme neden oldu; birkaç yıl sonra hobi olarak Haskell kurcalayınca ancak statik tiplerin de makul bir maliyet/fayda oranına sahip olabileceğini, asıl sorunun Java olduğunu anladım. “python is not java” makalesi de o karanlık dönemi iyi yansıtıyor
    • Kalıtım temelli alt tipleme daha da zayıftı. Kapsamlılık denetimi yapan pattern matching'in kullanılabilirliğini sunamıyor ve uygulamayı birden çok yere bölüyordu
    • Rakiplerden önce siteyi yayına alıp kullanıcıların önüne koymanın ve ölçek ekonomisini kilitlemenin önemli olduğu açıklaması, bugünün durumu için de oldukça tanıdık geliyor
  • Statik tiplerin zamanın ruhu haline gelmesinden sonra yazılımlarımızda güvenilirlik kazancını gerçekten görüp görmediğimizden şüpheliyim
    Statik tiplerin avantajının daha çok anlık geliştirme geri bildirimi ve ölümcül çalışma zamanı hatalarını azaltmakta olduğunu düşünüyordum; teoride böyle hatalar her zaman mümkün olsa da pratikte o kadar sık yaşanıyor gibi gelmiyordu

    • Gördüm. Azımsanmayacak büyüklükte bir TypeScript kod tabanında TypeScript hata sayısını 0 yapmayı hedeflemeye başladıktan sonra undefined ve null üzerinde metot çağırma girişimleri keskin biçimde azaldı
      Junior geliştiriciler ve bazı seniorlar başta her yere @ts-ignore serpileceği konusunda şüpheciydi, ama gerçekte kırık bağımlılık tiplerinden kaynaklananlar dahil toplamda üç kadar oldu. Eskiden geliştirme branch'inde tip karışıklığı yüzünden haftada bir uygulama çöküp işimi engellerdi; şimdi bunun en son ne zaman olduğunu bile hatırlamıyorum
      Sadece tscyi tatmin etmek bile, kodu bizzat benim yazmadığım durumlarda dahi tip kaynaklı bugları azalttı. Buna karşılık günümüzde lint araçları aşırı hevesli; Sonar gibi araçları memnun etmeye çalışırken gerçek refactor kırılmaları gördüm. Uyarıların %95'i sahteydi, %3'ü aracın kendi bug'ıydı, işe yarayan %2 bile gerçek bug'ın kök nedeni değildi. Kod tabanını kurallara uydurmak için 1 hafta harcayıp bir bug yakalamak yerine, o süreçte iki tane daha ekledim
      tscyi tatmin etme çalışması kabaca günde 2 gerçek bug düzeltmesi ve 1 regresyon üretti, ama regresyonlar genelde tam çökme değil, yanlış davranış düzeyinde yani daha düşük ciddiyetteydi
      Buna özellik tabanlı test de eklenince ortalama 2-4 saat sürüyordu ve her zaman en az bir bug ortaya çıkarıyordu. Kod özellik tabanlı teste uygunsa yapılmalı
      DeepSeek V4 Flash gibi ucuz bir modelle test kapsamını artırırken çöp testler üretilmemesine dikkat edince günde yaklaşık 2-3 mantık bug'ı düzelttim ve hiç çökme olmadı. Yine de test paketi zar zor bakım yapılabilir durumda
      Bir junior'ın Sonnet ve Opus 4.5, 4.6 ailesiyle kabaca test üretmesine izin verdiğimde modeller sadece “mevcut davranışı belgeleme” türünde testler yazdı; bu yüzden düzeltme etkisi küçüktü, test paketi de bakım yapılamaz hale geldiği için atılması gerekti
      Model tabanlı test bug yakalamada çok iyi, ama kurulumu karmaşık ve modeli yüzeydeki işlevlerde döngüye sokmadan köşelere doğru kazmaya yönlendirmek çok uğraştırıcı. Profil tabanlı model güdümlü bir fuzzer gibi bir şey ilginç olabilir
      Özetle tip denetleyicileri ölümcül hataları ve çeşitli karışıklıkları iyi yakalıyor, özellik tabanlı testler harika. Genel testler ise düzenli olarak karşılık almak için çok fazla disiplin gerektiriyor
    • Ben şahsen öyle olduğunu düşünüyorum. Kullandığım JavaScript'te null pointer bugları, TypeScript'e geçtikten sonra neredeyse önemsiz hale geldi; ekip arkadaşlarımda da durum benzerdi
  • Burada TypeScript'i iyi bir tip sistemiyle aynı sepete koymaya itiraz etmekte en çok zorlanıyorum

    • Evet. TypeScript sound değil ve özellikle await üzerinden tip daraltma biçimi beni birkaç kez vurdu. Yine de durumu dramatik biçimde iyileştirdiği doğru
      Dürüst olmak gerekirse yapısal tiplemeyi de sonunda benimsedim ve bunun gelecekteki dil tasarımını olumlu etkileyeceğini düşünüyorum
  • Bu iddia çok ikna edici değil. Cebirsel veri tipleri ve tip çıkarımı olan makul programlama dilleri 90'ların ortasından beri vardı
    Java ve C++'ın tip sistemleri çok zayıftı ama SML, OCaml ve Haskell zaten vardı ve bugünküne oldukça benzer hissettiriyordu. İnsanlar o dilleri kullanmadıysa bu kültür, benimsenme ve açıkça ifade edilmemiş gereksinimlerle ilgili bir sorundur; bunu sadece “kullanılabilir tip sistemleri yeterince iyi değildi” diye açıklayamayız
    Ya da kastedilen “o dönemin popüler dillerinin tip sistemleri kötüydü, bugünün popüler dillerinin tip sistemleri daha iyi, bu yüzden tip sistemleri daha popüler oldu” ise bu kulağa döngüsel bir argüman gibi geliyor
    Tip sistemleriyle birlikte tasarlanmış diller ile başta tipsiz tasarlanıp sonradan üzerine tip sistemi eklenmiş diller arasında da çok fazla nüans var

  • Aslen dinamik tipleri tercih eden biri olarak da bu yazının oldukça adil olduğunu düşünüyorum. Bugünlerde C# ile çalışıyorum, hobi olarak Lisp kullanıyorum ve eskiden Python da kullandım
    Java 5 kullanmak zorunda kaldığımda çoğu zaman kütüphane geliştiricilerinin kötü kararları yüzünden tip sistemiyle sürekli kavga ediyordum. 2010 civarında C#'a geçtikten sonra tip sistemi aktif olarak zararlı değildi ama çoğunlukla tekrarlıydı ve Python'da en yaygın tip karışıklığı olan null pointer exception'ı da engellemiyordu
    C#'ın tip sistemi gerçekten yardımcı olmaya ancak 2020 civarında nullable olmayan referans tipleri gelince başladı. Bu yıl yerel union tipleri de geliyor, ama exhaustiveness zorlayan union tipi kütüphaneleri en azından 2016'dan beri mümkündü ve ben kullanmaya 2020'de başladım
    Modanın hâlâ rol oynadığını düşünüyorum, ama bunun bir kısmı kötü değil. Daha ifade gücü yüksek tip sistemlerine sahip moda diller, para kazanmak için kullandığımız sıradan dillere de iyileştirmeler getirdi

  • Haskell ve onun tip sistemi aslında 2000’lerde de vardı. Bugünkü kadar yaygın kullanılmıyordu ama kesinlikle mevcuttu; bu yüzden bu iddianın o kısmı düzeltmesi gerekiyor
    Bana göre TypeScript, ana akım dil kullanıcılarını daha iyi bir tip sistemine alıştıran en büyük etkenlerden biriydi. Kalitesi ve Microsoft desteğinin yanı sıra JavaScript’e uygulanabilmesi de büyük avantajdı ve JavaScript’in tipe ihtiyacı Python’dan daha acildi. Bunun nedeni “Undefined is not a function.” ve “The good parts.”

    • Modern JavaScript sürümlerine uyarlanmış bir “good parts” kitabının, kısa ve öz kalmayı başararak çıkmasını isterdim
      “Real World Haskell” 2008’de çıktı ve amacı Haskell’i ana akım programcılara daha çekici göstermekti. İyi haberi yaymaya ne kadar yardımcı olduğunu bilmiyorum
      Java dünyasında Scala 2004’te etkileyici tipler getirdi, .NET tarafında ise F# 2005’te çıktı. Scala belki de Twitter gibi dikkat çeken kullanıcıları en çok kazanan dildi, ama TypeScript gibi o platformun kullanıcılarının büyük bir kısmını içine alabilecek bir konumda değildi; ayrıca Rust veya Go gibi başka dillerin kullanıcılarını kitlesel biçimde çekecek kadar da cazip değildi
    • Yazı bu meseleyi zaten ele alıyor. 90’lar ve 2000’lerin başında popüler olan erken dönem Java ya da C++98 gibi zayıf statik tip sistemlerini kâğıt küreğe benzetmişti
      Hemen sonraki paragrafta Haskell’den “modern tip sistemi” olarak söz ediyor, ama 90’ların sonu ile 2000’lerin başında Haskell deneyimi olanların oranı, kişisel olarak kurcalamış olanlar dahil edilse bile, fiilen %0’a yakındı. Yazı, o dönemde çoğu geliştiricinin statik tipli dilleri nasıl deneyimlediğini ve neden çoğunluğun statik tipli dillerden topluca kaçındığını anlatıyor
    • Bence Haskell ve OCaml bir ölçüde araç ekosisteminin zayıflığı yüzünden zorlanıyor. Dilin kendisi harika ama araç tarafındaki sayısız küçük pürüz yüzünden benimsenme kaybediyor
      Örneğin OCaml’da dune kullanmak için opam dosyasını, dune dosyasını, ocaml module sözdizimini ve ocaml sözdizimini anlamanız gerekiyor. Haskell’deki isteğe bağlı derleyici eklentileri de aynı derecede ürkütücü geliyor
      Bu, cargo tarafında yalnızca toml ve Rust bilmenin yeterli olmasıyla tezat oluşturuyor