2 puan yazan GN⁺ 2025-08-25 | 1 yorum | WhatsApp'ta paylaş
  • Zig 0.15 sürümünde yeni IO arayüzü (std.Io.Reader, std.Io.Writer) kullanıma sunuldu
  • Amaç, önceki IO yaklaşımındaki karmaşıklık ve performans sorunlarını iyileştirmekti; ancak gerçek kullanım biçimi konusunda kafa karışıklığı doğdu
  • tls.Client ve buffer kullanımıyla ilgili, parametrelerin tutarsız aktarım biçimi bu kafa karışıklığını artırıyor
  • En temel kullanım örneklerini kurarken bile farklı buffer boyutları ve seçenek alanları belirtme gibi karmaşık gereksinimler var
  • Resmî belgeler, kod örnekleri ve yardımcı fonksiyonların eksikliği nedeniyle bu yapı yeni başlayanlar için sezgisel değil

Zig 0.15'te sunulan yeni IO arayüzü ve arka planı

  • Zig 0.15 sürümünde std.Io.Reader ve std.Io.Writer adlı yeni IO tipleri kullanıma sunuldu
  • Önceki IO arayüzü, performans sorunları, tiplerin karışması ve anytype kullanımının aşırılığı nedeniyle karmaşıklığa yol açıyordu
  • Yeni IO yapısında arayüzler arasında daha net tip ayrımı ve performans iyileştirmesi başlıca hedefler

tls.Client ve IO arayüzü kullanımındaki pratik sorunlar

  • Mevcut smtp kütüphanesini güncellerken tls.Client.init fonksiyonunun kullanımında kafa karışıklığı yaşanıyor
  • Belgelerde init fonksiyonunun Reader ve Writer pointer'ları ile bir seçenekler kümesini argüman olarak aldığı belirtiliyor
  • Zig'in net.Stream yapısı sırasıyla reader() ve writer() metodlarıyla Stream.Reader/Writer döndürüyor
    • Ancak Stream.Reader/Writer ile std.Io.Reader/Writer tam olarak aynı tipler olmadığı için dönüştürme gerekiyor
    • Reader için interface() metodunu çağırmak, Writer içinse &interface alanını kullanmak gerektiğinden tutarlılık eksikliği hissediliyor
Reklam

Buffer ve seçenek alanlarını ayarlama sorunu

  • stream.writer, stream.reader sırasıyla buffer'ı argüman olarak alıyor
    • Buffer'ın yeni IO arayüzünde zorunlu bir unsur olduğu özellikle vurgulanıyor
  • tls.Client.init çağrısında ca_bundle, host, write_buffer, read_buffer gibi dört seçenek alanı mutlaka gerekli
    • Seçenek parametresi içinde verilen değerlerle doğrudan argüman olarak verilen değerlerin nasıl ayrıldığı belirsiz hissettiriyor
var tls_client = try std.crypto.tls.Client.init(
  reader.interface(),
  &writer.interface,
  .{
    .ca = .{.bundle = bundle},
    .host = .{ .explicit = "www.openmymind.net"; } ,
    .read_buffer = &read_buf2,
    .write_buffer = &write_buf2,
  },
)
  • Pratikte buffer pointer'ları doğru verilmezse program düzgün çalışmayabiliyor; takılma, çökme gibi çeşitli sorunlar ortaya çıkabiliyor
Reklam

Reader kullanırken sezgisellik sorunu

  • tls.Client'ın reader alanı kendi başına "şifresi çözülmüş akış" olsa da, gerçekte std.Io.Reader içinde alışıldık bir read metodu bulunmuyor
  • Bunun yerine peek, takeByteSigned, readSliceShort gibi daha az sezgisel metodlar sunuluyor
  • Kullanıma en yakın görünen API ise stream metodu üzerinden buffer'a veri okuma biçimi
var buf: [1024]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
const n = try tls_client.reader.stream(&w, .limited(buf.len));

Tüm kod örneği ve gerçek dünya sorunları

  • Baştan sona çalışan en küçük örneği oluşturmaya çalışırken bile seçenekler, buffer boyutları ve tip dönüşümleri gibi birçok ayrıntıya dikkat etmek gerekiyor
  • Test, belge ve örnek eksikliği öğrenme zorluğunu ve giriş bariyerini artırıyor
  • Zig dilindeki tutarlılığı ya da alttaki tasarımı yeterince anlamayanlar için garip gelen pek çok nokta var
  • Standart kütüphane içinde bile bu yaklaşım yaygın kullanılmadığından, gerçek kullanım için başvurulacak kaynaklar sınırlı

Deneyim ve sonuç

  • std.fmt.printInt gibi adlandırma değişiklikleri ve API tasarımındaki dönüşümler nedeniyle migration sürecinin kendisi de kolay değil
  • reader.interface(), &writer.interface kullanımı, seçeneklerin aktarım biçimi ve birden fazla buffer gerekliliği gibi tekrar eden birçok zorluk yaşanıyor
  • TLS gibi ağ/güvenlik protokollerine aşina olmayan biri açısından gereksinimleri kavramak daha da zor gelebiliyor
  • Genel olarak, önceki sürümlere kıyasla açıklık, dokümantasyon ve kullanım kolaylığı açısından hâlâ yetersiz kalan birçok nokta bulunuyor

1 yorum

 
GN⁺ 2025-08-25
Hacker News görüşü
  • Yazar olduğumu belirteyim. Sonunda düzgün çalışır hale getirdim. Hem şifreleme writer'ı hem de stream writer için flush süreci gerekiyormuş, aynı zamanda okuma tarafında da sorun varmış. Streaming çalışıyor ama Writer.Fixed, sendFile uygulamadığı için ilk okumada her zaman 0 döndürüyor. İlk çağrıdan sonra içerde streaming modundan okuma moduna geçiyor ve bir anda her şey çalışmaya başlıyor (ilgili kod bağlantısı: Zig File.zig #L1318). Şimdi websocket kütüphanesinde sıkıştırma özelliğini yeniden açmaya çalışıyorum

    • "Flush'ı unutmayın" YouTube meme'ini hatırlattı (YouTube videosu)

    • En az şaşırtma ilkesi (principle of least surprise) acaba nereye gitti diye merak ediyorum

    • Önceki arayüzden şu anki duruma gelinmiş olması gerçekten etkileyici. Büyük bir şaşkınlık var

  • Zig PM'i değilim ama OP'nin yaşadığı sorun için bariz ilk çözüm daha iyi dokümantasyon ve daha fazla kullanım örneği hazırlamak olurdu (çok fazla olması sorun değil). Bu tür bir çalışma, kullanıcılardan fazla şey isteyip istemediklerini sorgulamak için de iyi bir fırsat olabilir. Hedef mutlak performans ya da performans kaybı getiren soyutlamalardan kaçınmak idiyse, sanırım bu hedefe ulaşılmış; ama DX (geliştirici deneyimi) uzaya fırlamış gibi duruyor

    • Zig topluluğunun kültürünü pek bilmiyor gibisiniz. Dokümantasyon eksikliğinden şikâyet ederseniz, herkesin "stdlib kodunu kendin oku" diye yorum yapmaya hazır olduğunu görürsünüz. Çoğu API bu yazıdaki gibi kullanımı zor ve HTTP ya da dosya sistemi gibi temel işler bile tanıdık değilse gerçekten çok zorlayıcı oluyor. Bu yüzden gerçekten yetenekli olanlar ayakta kalıyor

    • Dokümantasyon yazmanın bir maliyeti var ve zaman alıyor. O süre içinde Zig'in başka kısımları da geliştirilebilir. Üzerinde çalışılan kodsa, tam olarak oturmadan dokümantasyonu ertelemek de makul bir tercih olabilir. Elbette dokümantasyon iyidir ama yeni özellikler, kritik hata düzeltmeleri ve dokümantasyon arasında öncelik belirlemek gerektiğinde her şeye aynı anda sahip olamazsınız

    • Zig, ne yapılmaması gerektiğini söylemeye fazla odaklanmış gibi görünüyor. Farklı yöntemleri ve kullanım örneklerini toplayıp iyi bir şekilde düzenleyen, anlatan bir yöne evrilmesini isterdim. Bu arayüzdeki eksik dokümantasyon bunun tipik bir örneği

    • İyi dokümantasyon ya da örnek yazmak ciddi emek istiyor. Şu an zig'de olan değişim hızına bakınca, daha tam oturmadan yazılan dokümanlar çok çabuk geçersiz hale geliyor

    • Zig geliştiricisi değilim ama Zig dokümantasyonunun fazla kısa olmasının nedenlerinden biri, dilin hâlâ genç ve sürekli evriliyor olması olabilir diye düşünüyorum. Bugün yazılan dokümanın yakında yanlış olacağını bile bile zaman ve enerji harcamanın zor geldiğini anlayabiliyorum

  • Zig dilinin kendisi gerçekten güzel, ama standart kütüphane hâlâ çok eksik, sürekli değişiyor, birçok açıdan yetersiz ve bazı kısımları ya fazla soyut ya da fazla düşük seviyeli. Şu anda standart kütüphane yerine doğrudan OS API'lerini kullanmanın daha iyi olduğunu düşünüyorum. Beta testeri olmaya hazır değilseniz standart kütüphaneden uzak durmanızı öneririm

    • Aslında ben de zig kullanırken çoğunlukla OS API'lerini tercih ediyorum. cImports iyi olduğu için zig tanımları yazmakla uğraşmak istemediğimde de rahatça kullanabiliyorum

    • Bana göre Zig aynı anda çok fazla şeyi yapmaya çalışıyor ve bu yüzden benim asgari kalite eşiğime bile ulaşamıyor. Kullanıcıları sert değişimlere ve deneylere katlanmaya zorlarken, yeterince insanın yatırım yapıp "1.0 öncesi bozuk olması sorun değil, bir gün düzelir" yanılsamasına inanmasına güveniyor gibi görünüyor (bence o gün hiç gelmeyecek). Deneylerinizin yükünü başkalarına bindirmek sağlıklı değil. Kararsız olduğunu önceden söylemiş olmanız ya da bağımlı olmayın demeniz de bir anda halının altından zemini çekilen insanlar için durumu değiştirmiyor. Zig'in tam olarak ne olduğunu anlamıyorum. Matklad buna machine level language diyor (ilgili röportaj: lobste.rs - Matklad röportajı), resmi sayfa ise robust, optimal, reusable general-purpose bir dil olduğunu söylüyor. Bunlar birbiriyle çelişiyor. Üstelik elle bellek yönetimi gerektirmeyen pek çok problem var; bu yüzden zig hiçbir zaman gerçekten genel amaçlı bir dil değil. Tüm bu karmaşa, zig'in kararsızlığında ve aşırı büyümüş standart kütüphanesinde kendini gösteriyor. Sadelik ve genel amaçlılık iddia ederken bu kadar büyük bir kütüphane çelişkili. Async de tüm platformlarda evrensel ve verimli biçimde uygulanabilecek bir özellik olmadığı halde sanki her derde deva gibi sunuldu. Eskiden fonksiyon renklendirme sorununu çözdüğü söylenerek tanıtılıyordu ama o girişim çoktan terk edildi. Şimdi tekrar başarabileceğine inanmamızı beklemek tuhaf. Aslında tüm platformlarda derleyici yapmak için gereken tek şey temel assembly talimatları; luajit parser'ını tamamen assembly ile yazdı ve her yerde gayet iyi çalışıyor. Ben çoğunlukla lua ile programlıyorum ve interpreter'da neredeyse hiç hataya rastlamadım. zig'in luajit'ten daha iyi çözeceği bir problem de aklıma gelmiyor. Zig ile çözülen bir şey varsa bile onu lua koduna embed edip FFI ile bağlamak mümkün. Çoğu kodun bu kadar düşük seviyeli optimizasyona ihtiyacı yok. Zig eklemek işleri daha çok baş ağrısına dönüştürüyor. Son zamanlarda zig etrafındaki abartı ile gerçeklik arasındaki fark neredeyse yapay zeka seviyesine ulaştı. Zig'e inanmak için, şu an sahip olmadığı yetenekleri bir gün kazanacağına dair boş bir umuda inanmanız gerekiyor. Ortada gerçek bir yürütme planı da yok; sadece "biraz daha bekleyin" deniyor

  • Bir kütüphane ya da arayüzün kendi tipimin buffer tahsisini istemesini anlamıyorum. Ben zaten parse edeceksem kütüphaneye gerek yok; kullanacaksam da bu, birlikte çalışabilirliği bozabilecek bir şey. Go'nun alışılmadık arayüzü, bazı arayüzlerin writer arayüzünü genişletmesi (örneğin hijacker arayüzü) ya da request nesnesinin farklı middleware'lerde çeşitli şekillerde yeniden kullanılması gibi nedenlerden geliyor. Kısacası request'in genişletilmesi gerekmez ama response, websocket veya tcp wrapper'ı gibi çok farklı biçimlere dönüşebilir

    • Kütüphanenin dış buffer tahsisi istemesi bana tuhaf gelmiyor. Bu, esneklik sağlıyor ama daha fazla elle iş yapmayı gerektiriyor. Örneğin hazır bir buffer havuzunuz varsa bunu yeniden kullanmak isteyebilirsiniz. Tip içeride kendi başına tahsis yaparsa bu mümkün olmaz. Ya da tüm kaynakların önceden tahsis edilmesi gereken ortamlarda sonradan tahsis yapılamaz. Dezavantajı şu: kullanıcıların yalnızca %10'u bu esnekliğe ihtiyaç duyarken, %90'ı sadece buffer tahsis edip verecek ve sonuçta herkes daha karmaşık bir iş yapmak zorunda kalacak. En iyi yaklaşım, yüksek esneklik sunarken basit senaryoları da kolay tutmaktır. Mesela 0 uzunlukta bir buffer (veya Zig'de null) verilirse tipin kendi tahsisini yapması sağlanabilir; ayrıca buffersız kolay oluşturma için ek bir constructor da sunulabilir. Tabii bunun dokümantasyon açısından zahmetli olacağı açık

    • Bu, her dilin seçtiği yerleşik kuralların farklı olmasına benziyor (radyan/derece gibi). Her türlü IO serbestçe dönüştürülebilir. Bir tarafta buna mock denir, başka bir dilde unsafeFoo adı verilebilir. Andrew Kelley, live stream'de Haskell topluluğunun 30 yılda tartıştığı örüntüleri kendi başına yeniden keşfediyor. Demek ki gelecek Zig'miş. Aydınlanmayı önce o yaşamış

    • Dış buffer'ın anlamı, fonksiyonun buffer tahsisini atlamasıdır

  • Zig yan projemi 0.15.x'e yükseltmeyi düşünmüyorum. Andrew'un neden bu sürümü çıkardığını ve yeni IO'yu erken benimseyenlerin eline vermesini anlıyorum. Ama readers/writers tarafındaki büyük değişikliklerin üzerinden sadece birkaç gün geçti. Standart kütüphane üzerinde çalışanlar için bu iyi olabilir, ama benim gibi hobi olarak zig kullananlar için 0.16.0'da stabil hale gelmesini beklemek daha mantıklı geliyor

    • Dilin adı Zig ise arada bir Zag da yapması gerekmez mi diye bir şaka geliyor aklıma

    • Zig çekirdek üyelerinden Loris Cro da yakın tarihli bir röportajda, IO değişikliklerinin sarsıntısı geçene kadar kendi projelerinde zig güncellemesini ertelediğini söylemişti. Ama sonrası için görünüm olumlu. Hem Andrew hem de Loris bunun son büyük değişiklik olacağını düşünüyor; bu da 1.0'ın çok uzakta olmayabileceği umudunu doğuruyor. Şu anda en büyük belirsizlik yalnızca yeniden gelecek stack-less coroutine etkisi gibi görünüyor

  • Yeni IO arayüzüyle ilgili yazıyı gördükten sonra zig'den uzak durmayı seçtim. Neyse ki içgüdülerimin doğru çıktığını düşünüyorum. Nedenlerim farklı olsa da sonuçta C++11 öncesinin ayrıntıcılığına benzer bir karmaşıklık hissi veriyor. Yeni bir dilin eskisinin yerine geçmeye çalışırken sonunda onun kadar karmaşık hale gelmesi gibi tanıdık bir örüntü tekrar ediyor

    • Bu tür gönderilerden birinin sahibi olduğumu da belirteyim. Böyle yazılar görüp zig denemekten korkmamak gerektiğini düşünüyorum. zig ekibi, daha iyi bir çözüm görürse cesurca değişiklik yapmaya hazır. Eğer zig'i gelecekte geçiminizi sağlayacağınız ana araç olarak görüyorsanız bu tür değişimler size uygun olmayabilir; ama bireysel geliştiriciler veya küçük ekipler için amacı net, tooling'i iyi, harika bir dil zaten
  • OP'nin, Stream.Readerstd.Io.Reader'a çevirmek için interface() metodunu çağırmak gerekirken Stream.Writer'dan std.io.Writer almak için &interface alanının adresinin gerekmesini tutarsız bulması, bana göre Go topluluğunda olsaydı baştan reddedilirdi. Go, küçük değişiklikler için bile olağanüstü derin analizler yaparak karar verir. En sevdiğim Go issue örneği: Go github issue #45624. Dört yıl tartışıp sonra sonuca varıyorlar. Yavaş olabilir ama tutarlılığı, tasarım düşüncesini ve gerçek kod kullanımını çok dikkatli ele alıyorlar. Yavaş ama gerektiği kadar yavaş olduklarını düşünüyorum. Bu şekilde alınan kararlar da sonuçta çok yüksek kaliteli oluyor

    • Rust da benzer. Sadece nightly rust'ta olup stable'da olmayan pek çok faydalı özellik var (örneğin generator). Sinir bozucu olabiliyor ama stable'a giren şeyler çok derin biçimde doğrulanmış oluyor. Ben sabırsızım ama Rust ekibinin yaklaşımını sağlıklı buluyorum

    • Go 1.0'dan önce o kadar yavaş değildi. Daha az köklü olsa da büyük değişiklikler sık yaşanıyordu (noktalı virgüllerin kaldırılması, error type değişiklikleri vb.) ve otomatik dönüştürme araçları da vardı. 1.0 ile birlikte kararlılık sözü verildi ve bugünkü yaklaşım benimsendi

  • Düşük seviyeli işler için aklıma gelen ilk dillerden biri Zig. Zig'i C/C++ cross-compiler olarak da kullanabilmek çok havalı

  • Sorunların çoğu temelde eksik ya da yetersiz dokümantasyondan kaynaklanıyor gibi görünüyor

    • Zig'de çok fazla şey çok sık değiştiği için dokümantasyon öncelik değil gibi görünüyor. Zig öğreticileri de neredeyse "örnek kod derlemesi" gibi duruyor (üstelik birçoğu güncel derleyicide çalışmıyor) ve standart kütüphanedeki pek çok tanım için doğrudan kaynak kod okumak gerekiyor. Zig sözdiziminin numaralarını öğrendiğinizde basit fonksiyonlar kısa, mantıklı ve isimler de açık olduğu için yazar açısından kolay. Allocator kavramı da kavramsal olarak o kadar zor değil, ama kendim yazmak istemem. Fakat karmaşık kavramlarda sınırlar net biçimde ortaya çıkıyor. Zig'in yeni IO sistemi, adeta Java'nın Streams/Readers/Writers yapısı gibi birçok katmanla sarılmış durumda. Basit çıktı için output.write("hello") kadar kolay görünmesi hedeflenmiş ama pratikte kullanım anlatımı yetersiz olduğu için kafa karıştırıyor. Bu kadar karmaşık bir tip sisteminin standart kütüphanede temsil edilmesi gerekip gerekmediği de şüpheli. Zig'in geneli açık, sade ve okunabilir metotlardan oluşurken yeni IO sistemi buna pek uymuyor ve sezgisel değil
  • (zig'in yeni sistemi) aslında sadece yürütme sınırlarını ayırmak için kullanılan bir kavramı tüm runtime motoruna karıştırıyor ve iki tarafın nasıl bağlanacağını da net biçimde göstermediği için sorun yaratıyor