Parse edin, doğrulamayın
Tip güdümlü tasarımın özü
- Tip güdümlü tasarımı (type-driven design) açıklayan basit bir slogan: Parse edin, doğrulamayın
- Bu slogan, tip sistemini kullanarak kodun güvenliğini ve doğruluğunu artırma yöntemini ifade eder
Olasılık alanı
- Statik tip sistemi, belirli bir fonksiyonun uygulanabilir olup olmadığını kolayca değerlendirmeyi sağlar
- Örnek:
foo :: Integer -> Void uygulanamaz (Void tipi bir değer taşıyamaz)
- Örnek:
head :: [a] -> a fonksiyonu, liste boş olduğunda tanımlı değildir
Kısmi fonksiyonları toplam fonksiyonlara dönüştürmek
Beklentiyi yönetmek
head fonksiyonu liste boş olduğunda bir değer döndüremediği için, Maybe tipi kullanılarak Nothing döndürülebilir
- Ancak bu, kullanım sırasında rahatsızlık yaratabilir
Beklentiyi aktarmak
NonEmpty tipi kullanılarak boş olmayan bir liste temsil edildiğinde, head fonksiyonunun her zaman bir değer döndürmesi garanti edilebilir
NonEmpty tipi kullanıldığında gereksiz kontroller kaldırılabilir ve hatalar tip sistemi sayesinde derleme zamanında yakalanabilir
Parse etmenin gücü
- Parse etme ile doğrulama arasındaki fark, bilginin nasıl korunduğunda yatar
validateNonEmpty fonksiyonu listenin boş olmadığını doğrular, ancak bu bilgiyi korumaz
parseNonEmpty fonksiyonu listenin boş olmadığını doğrular ve bilgiyi NonEmpty tipi olarak korur
Doğrulamanın riskleri
- Doğrulama temelli yaklaşım, "shotgun parsing" adlı bir soruna yol açabilir
- Bu, programın girdinin bir kısmını işledikten sonra kalan girdinin geçersiz olduğunu fark ettiği durumlara neden olabilir
- Parse etme, programı iki aşamaya ayırır; ilk aşamada girdinin geçerliliği kontrol edilir, ikinci aşamada ise yalnızca geçerli girdiler işlenir
Pratikte parse etme
- Veri tiplerine odaklanarak fonksiyonların tip imzalarını mümkün olduğunca somut hale getirin
- Yasadışı durumların ifade edilemediği veri yapıları kullanın ve veriyi mümkün olduğunca erken daha somut temsillere dönüştürün
- Veri tiplerinin koda yön vermesini sağlayın, kodun veri tiplerini kontrol etmesine değil
m () döndüren fonksiyonlar dikkatle kullanılmalıdır
- Veriyi birden çok kez parse etmekten korkmayın
- Verinin denormalize edilmiş temsillerinden kaçının; gerekirse bunları kapsülleme yoluyla yönetin
- Doğrulayıcıları parser gibi gösteren soyut veri tipleri kullanın
Özet, değerlendirme ve ilgili okumalar
- Haskell'in tip sisteminden mümkün olan en yüksek ölçüde yararlanmak zor değildir ve en yeni dil uzantılarını kullanmayı gerektirmez
- Temel fikir "toplam fonksiyon yazmak"tır; bu basittir ama uygulaması zor olabilir
- İlgili okumalar olarak Matt Parson'ın "Type Safety Back and Forth" başlıklı blog yazısı ile Matt Noonan'ın "Ghosts of Departed Proofs" makalesi önerilir
GN⁺ özeti
- Bu yazı, Haskell'in tip sistemini kullanarak kodun güvenliğini ve doğruluğunu artırma yöntemini açıklar
- Parse etme ile doğrulama arasındaki farkı anlamanın ve parse etme yoluyla girdinin geçerliliğini kontrol etmenin önemini vurgular
- Tip sisteminden yararlanarak yasadışı durumların ifade edilemediği veri yapıları kullanmanın ve veriyi mümkün olduğunca erken somut temsillere dönüştürmenin önemli olduğunu belirtir
- İlgili okumalar olarak Matt Parson'ın blog yazısı ile Matt Noonan'ın makalesini önerir
1 yorum
Hacker News görüşleri
Bu tavsiye ve makale çok faydalı
Statik olarak türlendirilmiş fonksiyonel diller kullanmayanlar için de yararlı
Bu fikir paradigmaları aşıyor
Benzer kavramlar 80'ler ve 90'lardaki nesne yönelimli literatürde de bulunabilir; örneğin Design by Contract
TypeScript genellikle çalışma zamanında türleri daraltacak şekilde yazılıyor
Design by Contract muhtemelen Clojure'un spec'ini etkilemiştir (Clojure dinamik bir dildir)
Özünde bu, varsayımlar ve garantilerle ilgilidir (gereksinimler ve sağlananlar)
Varsayımlar doğrulanıp garantiler sağlandığında, programın diğer bölümlerinde aynı varsayımları tekrar kontrol etmeye gerek kalmaz
Kodda zaten garanti altına alınmış özelliklerin yeniden kontrol edildiğini görmek kafa karıştırıcı olabilir; bu da kodu anlamayı ve iyileştirmeyi zorlaştırır
Bu desen modern C#'ta da iyi çalışıyor ve alan tasarrufu da sağlıyor
Güçlü bir tür sistemi kullanarak hata durumlarının ifade edilemez hale getirilmesi iyidir; bu, yazılım hatalarını azaltmaya yardımcı olur
Sorunu düşünmek ve tasarıma sadık kalmak daha fazla zaman alır, ancak çoğu durumda buna değer
"Parse, don’t validate" sloganı tür temelli tasarımı iyi özetliyor
Kişisel olarak, "geçerlilik kontrolünü her zaman yalnızca tek bir constructor içinde yapmak" daha iyi görünüyor; böylece geçersiz nesneler hiç var olamıyor
Bir nesneyi değiştirmek gerekirse, aynı constructor'ı yeniden çağırıp yeni durumu oluşturacak şekilde uygulanmalı
qmail'in 5. bölümünü hatırlatıyor; burada "parse etme" ve "iyi arayüz ile kullanıcı arayüzü vardır" gibi noktalar yer alıyor
Orta ölçekli bir programlama dersi veriyor olsaydım, öğrencilere bu önerileri karşılaştırıp zıtlaştıran bir makale yazdırırdım; her öneriden öğrenilecek şeyler var ve ilk bakışta çelişkili görünebilirler
İlgili kaynak: Richard Feldman'ın "Making Impossible States Impossible"
Önceki tartışmalar:
Crowdstrike'a iletildi
2000'lerin ortasındaki XML çılgınlığı sırasında birinin yaptığı yorumu hatırlatıyor; birçok kuruluşun XML'i seçmesinin nedeni, XML'in bir parser sağlamasıydı
Parser yazmanın zor olmadığı ve hatta eğlenceli olduğu düşünüldüğünde, insanların neden parser yazmak istemediğini anlayamıyorum
Bunun, Protocol Buffers'taki "required" anahtar sözcüğünün büyük bir hata olduğu görüşüne karşı mı çıktığını merak ediyorum
Hem esnek ve doğrulanmamış parsing hem de doğrulanmış parsing işlevlerine sahip olmak en iyisi olacaktır