3 puan yazan GN⁺ 2024-07-23 | 1 yorum | WhatsApp'ta paylaş

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

 
GN⁺ 2024-07-23
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

    • Örnek kod:
      if(!Whatever.TryParse<Thingy>(input, out var output)) output = some-sane-default;
      
    • Örnek kod:
      if(!Whatever.TryParse<Thingy>(input, out var output)) throw new ApplicationException($"Not a valid Thingy: {input}");
      
    • Kernel mod sürücülerinde ikincisinin kullanılmaması tavsiye edilir
  • 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