2 poin oleh GN⁺ 2025-08-25 | 1 komentar | Bagikan ke WhatsApp
  • Pada Zig versi 0.15, diperkenalkan antarmuka IO baru (std.Io.Reader, std.Io.Writer)
  • Tujuannya adalah memperbaiki kompleksitas metode IO lama dan masalah performa, tetapi justru menimbulkan kebingungan dalam penggunaan nyata
  • Terkait penggunaan tls.Client dan buffer, ketidakkonsistenan cara pengiriman parameter makin menambah kebingungan
  • Bahkan saat membuat contoh penggunaan dasar, ada tuntutan kompleks seperti menentukan berbagai ukuran buffer dan field opsi
  • Karena kurangnya dokumentasi resmi, contoh kode, dan fungsi bantu, antarmuka ini tidak terasa intuitif bagi pemula

Antarmuka IO baru yang diperkenalkan di Zig 0.15 dan latar belakangnya

  • Pada Zig 0.15, diperkenalkan tipe IO baru bernama std.Io.Reader dan std.Io.Writer
  • Antarmuka IO sebelumnya menimbulkan kompleksitas karena masalah performa, pencampuran tipe, dan penggunaan anytype yang berlebihan
  • Tujuan utama struktur IO baru ini adalah pemisahan tipe yang lebih jelas antar-antarmuka serta peningkatan performa

Masalah nyata saat menggunakan tls.Client dan antarmuka IO

  • Saat memperbarui library SMTP lama, muncul kebingungan dalam cara menggunakan fungsi tls.Client.init
  • Dalam dokumentasi, fungsi init disebut menerima pointer Reader dan Writer, serta satu set opsi sebagai argumen
  • net.Stream di Zig masing-masing mengembalikan Stream.Reader/Stream.Writer melalui metode reader() dan writer()
    • Namun Stream.Reader/Stream.Writer tidak persis sama tipenya dengan std.Io.Reader/std.Io.Writer, sehingga perlu konversi
    • Untuk Reader harus memanggil metode interface(), sedangkan untuk Writer harus memakai field &interface, sehingga terasa kurang konsisten

Masalah pengaturan buffer dan field opsi

  • stream.writer dan stream.reader masing-masing menerima buffer sebagai argumen
    • Buffer ditekankan sebagai elemen yang wajib dalam antarmuka IO baru
  • Saat memanggil tls.Client.init, empat field opsi seperti ca_bundle, host, write_buffer, dan read_buffer wajib disediakan
    • Aturan pemisahan antara nilai yang dikirim lewat parameter opsi dan nilai yang diberikan langsung sebagai argumen terasa tidak jelas
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,
  },
)
  • Dalam praktiknya, jika pointer buffer tidak diberikan dengan benar, program bisa gagal berjalan sebagaimana mestinya atau mengalami hang, crash, dan berbagai masalah lain

Masalah keintuitifan saat menggunakan Reader

  • Meskipun field reader pada tls.Client sendiri merupakan "stream yang sudah didekripsi", std.Io.Reader ternyata tidak memiliki metode read yang umum dipakai
  • Sebagai gantinya, yang tersedia hanya metode yang kurang intuitif seperti peek, takeByteSigned, dan readSliceShort
  • API yang paling mendekati penggunaan umum justru memakai metode stream untuk membaca data ke buffer
var buf: [1024]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
const n = try tls_client.reader.stream(&w, .limited(buf.len));

Contoh kode lengkap dan masalah di dunia nyata

  • Bahkan untuk membuat contoh minimum yang benar-benar berjalan, ada banyak hal yang harus diperhatikan seperti opsi, ukuran buffer, dan konversi tipe
  • Karena minimnya test/dokumentasi/contoh, tingkat kesulitan belajar dan hambatan masuk menjadi tinggi
  • Jika belum memahami konsistensi di dalam bahasa Zig atau underlying design-nya, ada banyak bagian yang terasa janggal
  • Bahkan di dalam standard library sendiri, pola ini belum banyak digunakan sehingga referensi praktik nyata juga terbatas

Pengalaman dan kesimpulan

  • Perubahan nama seperti std.fmt.printInt dan perubahan desain API membuat proses migrasi sendiri tidak mudah
  • Berbagai kesulitan terus berulang, seperti pola reader.interface(), &writer.interface, cara pengiriman opsi, dan kebutuhan akan beberapa buffer sekaligus
  • Dari sudut pandang orang yang belum terbiasa dengan protokol jaringan/keamanan seperti TLS, memahami persyaratan ini terasa lebih sulit lagi
  • Secara keseluruhan, dibanding sebelumnya, masih ada banyak bagian yang belum memadai dari sisi kejelasan, dokumentasi, dan kemudahan penggunaan

1 komentar

 
GN⁺ 2025-08-25
Opini Hacker News
  • Menyatakan diri sebagai Author. Akhirnya berhasil membuatnya bekerja dengan benar. Ternyata baik encrypted writer maupun stream writer sama-sama perlu proses flush, dan di saat yang sama ada masalah di sisi pembacaan juga. Streaming memang berjalan, tetapi karena Writer.Fixed tidak mengimplementasikan sendFile, pembacaan pertama selalu mengembalikan 0. Setelah panggilan pertama, secara internal ia berpindah dari mode streaming ke mode baca, lalu tiba-tiba semuanya mulai bekerja (tautan kode terkait: Zig File.zig #L1318). Sekarang sedang berusaha mengaktifkan kembali fitur kompresi di library websocket

    • Jadi teringat meme YouTube “Jangan lupa flush” (video YouTube)

    • Jadi penasaran ke mana perginya principle of least surprise

    • Rasanya luar biasa juga bisa berpindah dari antarmuka sebelumnya ke situasi sekarang. Tingkat keterkejutannya besar

  • Bukan PM Zig, tetapi solusi pertama yang paling jelas untuk masalah yang dialami OP adalah dokumentasi yang lebih baik dan lebih banyak contoh penggunaan (tidak masalah kalau jumlahnya sangat banyak). Ini juga bisa menjadi kesempatan bagus untuk, sambil mengerjakan itu, meninjau apakah terlalu banyak hal dibebankan ke pengguna. Jika tujuan yang dikejar adalah performa absolut atau menghindari pengenalan abstraksi yang menurunkan performa, sepertinya tujuan itu tercapai, tetapi DX (pengalaman pengembang) terasa seperti sudah melayang ke galaksi lain

    • Sepertinya tidak terlalu memahami budaya komunitas Zig. Kalau mengeluhkan kurangnya dokumentasi, siapa pun harus siap dibanjiri komentar “baca langsung kode stdlib”. Sebagian besar API sulit digunakan seperti yang terlihat di tulisan ini, dan bahkan tugas dasar seperti HTTP atau sistem file bisa sangat berat kalau belum terbiasa. Jadi yang bertahan benar-benar hanya orang yang sangat mahir

    • Menulis dokumentasi memang butuh biaya dan waktu. Waktu itu juga bisa dipakai untuk memperbaiki bagian lain dari Zig. Kalau kodenya masih dalam proses pengerjaan, menunda dokumentasi sampai lebih matang juga pilihan yang masuk akal. Tentu dokumentasi itu bagus, tetapi ketika harus memilih prioritas antara fitur baru, perbaikan bug penting, atau pekerjaan dokumentasi, tidak selalu mungkin mendapatkan semuanya sekaligus

    • zig tampaknya terlalu fokus pada memberi tahu apa yang tidak boleh dilakukan. Akan lebih baik kalau berkembang ke arah mengumpulkan berbagai cara dan contoh penggunaan, lalu menyusunnya dengan baik dan menjelaskannya. Ketiadaan dokumentasi untuk antarmuka ini adalah contoh yang sangat representatif

    • Menulis dokumentasi atau contoh yang bagus membutuhkan banyak usaha. Melihat luasnya perubahan yang sedang terjadi di zig sekarang, membuat dokumentasi sebelum semuanya benar-benar mapan pun cepat jadi tidak berguna

    • Saya bukan pengembang Zig, tetapi saya kira salah satu alasan dokumentasi Zig terlalu ringkas adalah karena bahasanya masih muda dan terus berevolusi. Bisa dipahami kalau sulit menginvestasikan waktu dan energi ketika tahu dokumentasi yang ditulis sekarang sebentar lagi bisa menjadi salah di masa depan

  • Bahasa Zig itu sendiri sebenarnya sangat bagus, tetapi standard library-nya masih sangat belum matang, terus berubah, banyak kekurangan, dan sebagian terlalu abstrak atau justru terlalu low-level. Saat ini saya rasa lebih baik memakai API OS secara langsung daripada standard library. Kalau tidak siap jadi beta tester, saya sarankan menghindari standard library

    • Memang, saat saya memakai zig, biasanya saya juga lebih banyak memakai API OS. cImports bekerja dengan baik, jadi ketika malas membuat definisi zig sendiri pun tetap mudah dipakai

    • Menurut saya Zig cenderung mencoba melakukan terlalu banyak hal sekaligus, sehingga bahkan tidak mencapai ambang kualitas minimum versi saya. Dari cara pengguna dipaksa menanggung perubahan drastis dan eksperimen, situasinya jadi seperti cukup banyak orang sudah terlanjur berinvestasi sehingga mereka harus percaya pada ilusi “tidak apa-apa rusak sebelum 1.0, nanti juga akan bagus” (kesimpulannya: rasanya hari itu tidak akan pernah datang). Saya tidak menganggap baik membebankan eksperimen sendiri kepada orang lain. Walaupun sudah diberi tahu sejak awal bahwa ini tidak stabil, walaupun sudah dibilang agar jangan bergantung padanya, tetap saja bermasalah bagi orang yang akhirnya terkena rug pull (tiba-tiba semuanya berubah). Saya juga tidak yakin sebenarnya zig itu apa. Matklad menyebutnya machine level language (wawancara terkait: lobste.rs - wawancara Matklad), sementara halaman resminya menyebutnya bahasa general-purpose yang robust, optimal, reusable. Keduanya saling bertentangan. Dan banyak masalah yang sebenarnya tidak memerlukan manajemen memori manual, jadi zig sama sekali bukan bahasa serbaguna. Pada akhirnya semua kekacauan ini tampak pada ketidakstabilan zig dan standard library-nya yang membengkak. Mengklaim kesederhanaan dan sifat general-purpose sambil memiliki library sebesar ini terasa kontradiktif. Async juga dijanjikan seolah solusi universal, padahal itu fitur yang tidak bisa diimplementasikan secara universal dan efisien di semua platform. Dulu bahkan dipromosikan sebagai solusi untuk function coloring, tetapi upaya itu sekarang sudah ditinggalkan. Logika bahwa kita harus percaya lagi bahwa mereka bisa melakukannya dengan benar terasa aneh. Sebenarnya untuk mengimplementasikan compiler di semua platform, yang dibutuhkan hanya instruksi assembly dasar, dan luajit bahkan mengimplementasikan parser sepenuhnya dalam pure assembly dan tetap berjalan baik di mana-mana. Saya sendiri kebanyakan memprogram dengan lua, dan hampir tidak pernah menemui bug di interpreter. Saya juga tidak bisa memikirkan masalah apa yang akan diselesaikan zig lebih baik daripada luajit. Kalaupun ada sesuatu yang hanya bisa diselesaikan dengan zig, itu bisa di-embed ke dalam kode lua dan dihubungkan lewat FFI. Sebagian besar kode tidak benar-benar membutuhkan optimisasi low-level sebesar itu. Mengadopsi zig justru membuat kepala pusing. Belakangan hype berlebihan terhadap zig terasa mencapai tingkat kesenjangan antara realitas dan janji seperti AI. Untuk percaya pada zig, kita harus percaya pada harapan kosong bahwa ia suatu hari akan memiliki kemampuan yang saat ini belum ada. Tidak ada rencana eksekusi nyata, hanya semacam “tunggu sebentar lagi”

  • Saya tidak paham kenapa library atau antarmuka harus meminta alokasi buffer dari tipe saya. Kalau saya yang melakukan parsing, berarti saya sebenarnya tidak perlu library itu, dan kalau saya tetap memakainya, itu juga bisa merusak interoperabilitas. Antarmuka khas go seperti itu ada karena beberapa antarmuka memang memperluas writer interface (lihat antarmuka hijacker), atau karena objek request dipakai ulang dengan berbagai cara di banyak middleware. Singkatnya, request tidak perlu diperluas, tetapi response bisa berubah menjadi banyak bentuk seperti websocket, wrapper tcp, dan sebagainya

    • Tidak terasa aneh bagi saya jika library meminta alokasi buffer eksternal. Itu memberi fleksibilitas dengan konsekuensi lebih banyak kerja manual. Misalnya, kalau sudah punya buffer pool sendiri, Anda mungkin ingin memakainya kembali. Kalau tipenya mengalokasikan sendiri secara internal, itu tidak mungkin dilakukan. Atau dalam lingkungan yang semua resource-nya harus dialokasikan lebih dulu, alokasi belakangan tidak dimungkinkan. Kekurangannya adalah hanya 10% pengguna yang butuh fleksibilitas seperti ini, sementara 90% lainnya mungkin hanya akan mengalokasikan buffer lalu menyerahkannya, tetapi semua orang jadi harus menghadapi hal yang lebih rumit. Cara terbaik adalah memberi fleksibilitas tinggi sambil tetap memudahkan kasus sederhana. Misalnya, kirim buffer panjang 0 (atau null di Zig) agar tipenya mengalokasikan sendiri, dan sediakan juga constructor untuk membuatnya dengan mudah tanpa buffer tambahan. Tentu saja, jelas bagian seperti ini akan sangat merepotkan untuk didokumentasikan

    • Ini mirip perbedaan konvensi yang dipilih tiap bahasa (seperti radian/derajat, dan sebagainya). IO apa pun bisa diubah secara bebas. Di satu sisi mungkin disebut mock, di bahasa lain bisa disebut unsafeFoo. Andrew Kelley menemukan kembali secara mandiri pola yang sudah dibahas komunitas Haskell selama 30 tahun dalam live stream. Jadi masa depan adalah Zig. Dia yang lebih dulu tercerahkan

    • Makna buffer eksternal adalah fungsi tersebut melewatkan alokasi buffer

  • Saya tidak berniat menaikkan side project zig saya ke 0.15.x. Saya paham kenapa Andrew memilih merilisnya, dan saya menghargai keputusan menyerahkan Io baru ke tangan para early adopter. Tapi ini baru beberapa hari setelah perubahan besar readers/writers. Bagi orang yang mengerjakan standard library mungkin ini bagus, tetapi bagi orang seperti saya yang memakai zig sebagai hobi, rasanya lebih bijak menunggu sampai 0.16.0 setelah semuanya lebih stabil

    • Kalau nama bahasanya Zig, kadang terpikir lelucon bahwa sesekali seharusnya juga perlu Zag

    • Loris Cro, anggota inti Zig, juga baru-baru ini menyebut dalam wawancara bahwa ia menunda memperbarui zig di proyeknya sendiri sampai gejolak perubahan IO mereda. Tetapi prospeknya setelah itu positif. Andrew dan Loris sama-sama melihat ini sebagai perubahan besar terakhir, jadi ada harapan bahwa 1.0 tidak akan terlalu lama lagi. Satu-satunya variabel terbesar saat ini adalah dampak dari stack-less coroutine yang akan diperkenalkan kembali

  • Setelah membaca tulisan tentang antarmuka IO baru, saya memilih menghindari zig. Untungnya naluri saya tampaknya benar. Meski alasannya berbeda, pada akhirnya terasa ada kompleksitas yang mirip dengan verbositas pra-C++11. Pola lama yang familier terulang lagi: bahasa baru yang mencoba menjadi pengganti akhirnya menjadi sama rumitnya dengan bahasa lama

    • Saya nyatakan juga bahwa postingan saya termasuk salah satunya. Saya tidak merasa orang perlu takut mencoba zig hanya karena membaca tulisan seperti ini. Tim zig siap mengubah segalanya secara berani kalau melihat solusi yang lebih baik. Kalau Anda menganggap zig sebagai investasi masa depan, perubahan seperti ini mungkin tidak cocok, tetapi untuk individu atau tim kecil ini sudah merupakan bahasa yang keren dengan tujuan yang jelas dan tooling yang bagus
  • Saya rasa penunjukan OP bahwa untuk mengubah Stream.Reader menjadi std.Io.Reader harus memanggil metode interface(), tetapi untuk mendapatkan std.io.Writer dari Stream.Writer perlu mengambil alamat dari field &interface, adalah ketidakkonsistenan yang komunitas Go pasti akan tolak mentah-mentah. Go cenderung memutuskan bahkan perubahan kecil sekalipun setelah analisis yang sangat mendalam. Contoh issue Go favorit saya: Go github issue #45624. Diskusinya berlangsung 4 tahun sebelum mengambil kesimpulan. Mungkin lambat, tetapi konsistensi, pertimbangan desain, dan penggunaan pada kode nyata diperiksa dengan teliti. Lambat, tetapi menurut saya memang secepat itu yang dibutuhkan. Keputusan yang lahir dari proses seperti itu pada akhirnya berkualitas sangat tinggi

    • Rust juga sama. Ada banyak fitur berguna yang hanya ada di nightly rust dan belum masuk stable (misalnya generator). Memang bikin frustrasi, tetapi fitur yang masuk ke stable sudah diverifikasi sangat mendalam. Saya sendiri tidak sabaran, tetapi saya rasa pendekatan tim Rust itu patut dicontoh

    • Sebelum Go 1.0, situasinya tidak selambat itu. Perubahan besar cukup sering terjadi walaupun mungkin tidak terlalu fundamental (penghapusan titik koma, perubahan tipe error, dan sebagainya), dan ada dukungan alat konversi otomatis juga. Sejak 1.0 mereka menjanjikan stabilitas, dan sejak itu pendekatannya menjadi seperti sekarang

  • Zig adalah bahasa pertama yang terlintas di pikiran untuk pekerjaan low-level. Fakta bahwa Zig juga bisa dipakai sebagai cross compiler C/C++ sangat keren

  • Sebagian besar masalah ini tampaknya benar-benar berasal dari dokumentasi yang kurang atau buruk

    • Karena terlalu banyak hal di Zig yang sering berubah, tampaknya dokumentasi bukan prioritas. Bahkan tutorial Zig pun hampir terasa seperti “kumpulan contoh kode” (dan banyak contoh tidak berjalan di compiler terbaru), dan banyak definisi standard library juga mengharuskan membaca source code langsung. Kalau sudah memahami semua trik sintaks Zig, fungsi-fungsi sederhana memang singkat, logis, dan penamaannya jelas, jadi mudah bagi penulisnya. Konsep Allocators juga secara konsep tidak terlalu sulit, walaupun saya sendiri tidak ingin membuatnya. Tetapi pada konsep yang lebih rumit, batasannya terlihat jelas. Sistem IO baru Zig terasa seperti struktur Streams/Readers/Writers di Java yang dibungkus dalam banyak lapisan. Ia berusaha membuat output sederhana bisa ditulis seperti output.write("hello"), tetapi pada praktiknya justru membingungkan karena penjelasan penggunaannya kurang. Saya juga ragu apakah sistem tipe serumit ini memang perlu diekspresikan di standard library. Secara keseluruhan Zig dipenuhi metode yang jelas, ringkas, dan mudah dibaca, tetapi sistem IO baru ini terasa jauh dari itu dan tidak intuitif
  • (sistem baru zig) bermasalah karena mencampurkan konsep yang tadinya hanya untuk membagi batas eksekusi ke dalam seluruh mesin runtime, tanpa menjelaskan dengan jelas bagaimana menghubungkan kedua sisinya