Statik Tipler ve Kürek
(carefully.understood.systems)- 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çinT | null, Swift içinT?, Rust içinOptional<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 anlayabiliyorsalet 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
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
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
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
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
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
undefinedvenullüzerinde metot çağırma girişimleri keskin biçimde azaldıJunior geliştiriciler ve bazı seniorlar başta her yere
@ts-ignoreserpileceğ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ıyorumSadece
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 ekledimtscyi 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 ciddiyetteydiBuna ö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
Burada TypeScript'i iyi bir tip sistemiyle aynı sepete koymaya itiraz etmekte en çok zorlanıyorum
awaitüzerinden tip daraltma biçimi beni birkaç kez vurdu. Yine de durumu dramatik biçimde iyileştirdiği doğruDü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.”
“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
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
Örneğin OCaml’da
dunekullanmak içinopamdosyasını,dunedosyasını,ocaml modulesözdizimini veocamlsözdizimini anlamanız gerekiyor. Haskell’deki isteğe bağlı derleyici eklentileri de aynı derecede ürkütücü geliyorBu,
cargotarafında yalnızcatomlve Rust bilmenin yeterli olmasıyla tezat oluşturuyor