7 poin oleh GN⁺ 2025-08-22 | 1 komentar | Bagikan ke WhatsApp
  • Zig berbasis sintaks berbasis kurung kurawal yang mirip Rust, tetapi ditingkatkan dengan semantik bahasa yang lebih sederhana dan pilihan sintaks yang lebih rapi
  • Literal bilangan bulat semuanya dimulai sebagai comptime_int dan dikonversi secara eksplisit saat ditugaskan, sementara literal string menggunakan notasi string mentah ringkas berbasis \\
  • Literal record dalam bentuk .x = 1 memudahkan pencarian penulisan field, dan semua tipe direpresentasikan secara konsisten dengan notasi prefiks
  • and·or digunakan sebagai keyword alur kontrol, dan konstruksi if·loop dapat menghilangkan kurung kurawal secara opsional, dengan formatter yang menjamin keamanan
  • Tanpa namespace, semua hal diperlakukan sebagai ekspresi untuk menyatukan sintaks tipe·nilai·pola, serta memanfaatkan generic·literal record·fungsi bawaan (@import, @as, dll.) secara ringkas

Ikhtisar

  • Zig memiliki tampilan yang mirip Rust, tetapi mengadopsi struktur bahasa yang lebih sederhana
  • Desain sintaksnya berfokus pada ramah grep, konsistensi sintaks, dan mengurangi noise visual yang tidak perlu

Literal bilangan bulat

const an_integer = 92;  
assert(@TypeOf(an_integer) == comptime_int);  
  
const x: i32 = 92;  
const y = @as(i32, 92);  
  • Semua literal bilangan bulat bertipe comptime_int
  • Saat ditugaskan ke variabel, tipe harus ditentukan secara eksplisit atau dikonversi dengan @as
  • Bentuk var x = 92; tidak akan berfungsi dan memerlukan tipe eksplisit

Literal string

const raw =  
    \\Roses are red  
    \\  Violets are blue,  
    \\Sugar is sweet  
    \\  And so are you.  
    \\  
;  
  • Setiap baris adalah token terpisah sehingga tidak ada masalah indentasi
  • Tidak perlu melakukan escape pada \\ itu sendiri

Literal record

const p: Point = .{  
    .x = 1,  
    .y = 2,  
};  
  • Format .x = 1 menguntungkan untuk membedakan baca/tulis
  • Notasi .{} membedakannya dari blok sambil otomatis dikonversi ke tipe hasil

Notasi tipe

u32        // bilangan bulat  
[3]u32     // array panjang 3  
?[3]u32    // array yang bisa null  
*const ?[3]u32 // pointer konstan  
  • Semua tipe memakai notasi prefiks
  • Dereferensi memakai notasi sufiks (ptr.*)

Identifier

const @"a name with space" = 42;  
  • Dapat mencegah konflik keyword atau memberi nama khusus

Deklarasi fungsi

pub fn main() void {}  
fn add(x: i32, y: i32) i32 {  
    return x + y;  
}  
  • Keyword fn dan nama fungsi berdempetan sehingga mudah dicari
  • Tidak memakai -> untuk menuliskan tipe kembalian

Deklarasi variabel

const mid = lo + @divFloor(hi - lo, 2);  
var count: u32 = 0;  
  • Menggunakan const dan var
  • Penulisan tipe mengikuti urutan nama: tipe

Alur kontrol: and/or

while (count > 0 and ascii.isWhitespace(buffer[count - 1])) {  
    count -= 1;  
}  
  • and, or adalah keyword alur kontrol
  • Operasi bit menggunakan &, |

Pernyataan if

.direction = if (prng.boolean()) .ascending else .descending;  
  • Tanda kurung wajib, kurung kurawal opsional
  • zig fmt menjamin format yang aman

Perulangan

for (0..10) |i| {  
    print("{d}\n", .{i});  
} else @panic("loop safety counter exceeded");  
  • for dan while sama-sama mendukung klausa else
  • Iterator dan nama elemen ditempatkan secara intuitif

Namespace dan resolusi nama

const std = @import("std");  
const ArrayList = std.ArrayList;  
  • Shadowing variabel dilarang
  • Tidak ada namespace maupun glob import

Semua adalah ekspresi

const E = enum { a, b };  
const e: if (true) E else void = .a;  
  • Menyatukan sintaks tipe·nilai·pola
  • Ekspresi kondisional dapat ditempatkan di posisi tipe

Generic

fn ArrayListType(comptime T: type) type {  
    return struct {  
        fn init() void {}  
    };  
}  
  
var xs: ArrayListType(u32) = .init();  
  • Generic diekspresikan dengan sintaks pemanggilan fungsi (Type(T))
  • Argumen tipe selalu eksplisit

Fungsi bawaan

const foo = @import("./foo.zig");  
const num = @as(i32, 92);  
  • Memanggil fitur yang disediakan compiler dengan prefiks @
  • @import menampilkan jalur file dengan jelas
  • Argumennya harus berupa literal string

Kesimpulan

  • Sintaks Zig adalah contoh bagaimana sekumpulan pilihan kecil dapat membentuk bahasa yang enak dibaca
  • Jika jumlah fitur dikurangi, sintaks yang dibutuhkan juga berkurang, dan kemungkinan konflik antarsintaks pun menurun
  • Meminjam ide bagus dari bahasa yang sudah ada, tetapi berani memperkenalkan sintaks baru saat diperlukan

1 komentar

 
GN⁺ 2025-08-22
Komentar Hacker News
  • Tulisan ini membahas dengan sangat mendalam berbagai trade-off yang muncul dalam perancangan sintaks, dan saya merasa sangat terkesan dengan minimalisme, konsistensi, serta fokus Zig yang nyaris tanpa ampun pada keterbacaan. Yang saya suka, ini bukan keindahan yang abstrak, melainkan semacam 'brutalisme' yang tidak menghadirkan kejutan dalam penggunaan industri. Perancangan sintaks yang seimbang seperti ini benar-benar langka, dan menurut saya Zig berhasil melakukannya dengan baik

    • Agak disayangkan artikel ini tidak menyinggung penanganan error. Pendekatan try/catch di Zig sangat bagus, sampai-sampai itu menjadi cara penanganan error favorit saya di antara banyak bahasa. Akan lebih baik kalau bagian ini juga diperkenalkan

    • Daya tarik sejati Zig bukanlah 'keterbacaan yang indah di permukaan', melainkan keindahan yang konsisten yang didapat melalui abstraksi. Seperti analogi S-expression dan M-expression, pendekatan yang baik untuk kasus umum sering kali lebih unggul dalam jangka panjang daripada desain khusus untuk banyak situasi pengecualian. Jika Anda terus menambahkan kasus pengecualian seperti di C++, pada akhirnya beban menghafal semua aturan hanya akan makin besar. Dalam desain bahasa, jika kita mengejar kesederhanaan dan konsistensi, kompleksitas bisa saja berpindah ke pengguna hingga jatuh ke 'Turing tarpit', jadi yang penting adalah pendekatan di mana kasus khusus terselesaikan secara alami dari aturan umum. Contoh seperti ini juga bisa dilihat di komik XKCD New Pet

    • Kalau ada contoh yang menurut Anda berkesan, saya penasaran apakah Anda bisa membagikannya

  • Soal Zig yang menggunakan gaya anotasi tipe 'nama:tipe' seperti Rust, saya justru lebih menyukai cara tradisional di mana tipe ditulis lebih dulu. Saat saya kembali memeriksa deklarasi variabel, hal yang paling ingin saya ketahui adalah tipe variabel itu, dan kalau saya tidak bisa menemukannya dengan cepat rasanya tidak nyaman. Terutama di Rust, ada banyak elemen yang terasa berulang dan tidak perlu seperti let mut, sehingga malah merepotkan, dan saya juga suka gaya C atau C++ yang menempatkan tipe lebih dulu. Dalam praktiknya, menurut saya idealnya inferensi tipe dipakai seminimal mungkin hanya di tempat yang memang membutuhkannya

    • Kata kunci let memang juga berguna karena dengan jelas menunjukkan bahwa itu adalah sebuah pernyataan deklarasi. Tanpa itu, Anda bisa mengalami masalah parsing sintaks ambigu seperti di C++

    • Saya juga selalu cenderung memeriksa tipe variabel lebih dulu, jadi saya lebih suka gaya dengan tipe di depan. Dari sudut pandang parser, memproses nama lebih dulu memang lebih mudah, dan saya paham TypeScript mengadopsi struktur ini demi kompatibilitas dengan JavaScript. Pada akhirnya yang penting menurut saya adalah standard library yang mudah digunakan. Seperti pada contoh penyalahgunaan sistem tipe secara berlebihan, menyampaikan niat dengan jelas lebih penting daripada memaksakan semua state diekspresikan sebagai tipe

    • Saya memang sering menggulir ke atas untuk memeriksa tipe variabel di kode, tetapi justru kalau tipe ditulis lebih dulu, lebih sulit menemukan deklarasi variabel yang saya cari. Nama tipe berada paling depan dan panjangnya bervariasi, jadi mata harus bergerak bolak-balik ke kiri dan kanan, yang terasa tidak efisien

    • Dalam banyak kasus editor akan langsung menampilkan informasi tipe saat kursor diarahkan ke sana, jadi posisi tipe di kode mungkin tidak terlalu penting. Rust verbose sebagian besar karena alasan implementasi untuk menghindari ambiguitas parsing. Kalau tipe ditulis di depan seperti C atau C++, variabel yang dideklarasikan dengan nama tertentu jadi lebih sulit dicari dengan grep, dan gaya menaruh return type di depan memang diperkenalkan karena template, tetapi dalam beberapa kasus justru membuat kode lebih mudah dibaca dan dicari

    • Secara pribadi saya lebih menyukai gaya anotasi tipe ala Pascal. Bahkan saat memakai inferensi tipe pun tidak perlu fitur pelengkap seperti 'auto', dan dari sudut pandang parsing juga lebih tidak ambigu. Dalam MyClass x, sulit langsung tahu apakah MyClass itu tipe atau nama variabel, jadi pendekatan ini membantu mengurangi ambiguitas semacam itu

  • Untuk sintaks raw/multiline string Zig, cara yang mengharuskan menulis \\ berkali-kali terasa terlalu membingungkan dan ekstrem

    • Kalau Anda pernah memformat multiline string di Python, C++, atau Rust, Anda akan mengerti kerepotannya. Selalu ada masalah apakah indentasi ikut masuk ke isi string, dan kalau seperti YAML yang punya mode penghapusan indentasi, itu justru bisa menambah kebingungan. Pendekatan Zig sangat jelas dalam hal indentasi

    • Awalnya sintaks ini terasa sangat tidak nyaman, tetapi setelah memakai Zig, pelan-pelan jadi terbiasa dan justru mulai terlihat kelebihannya. Zig memang menarik karena saat pertama kali ditemui bisa menimbulkan kesan tidak suka, tetapi setelah benar-benar dipakai, Anda jadi menyadari keunggulannya

    • Sebenarnya ini bukan sintaks yang gila, melainkan masalahnya yang gila dan rumit: bagaimana menyisipkan multiline string lain dengan aman di dalam multiline string. Di Zig, saya suka karena tidak perlu escape tambahan dan tidak perlu khawatir soal indentasi

    • trimIndent di Kotlin, text block di Go atau Java, dan terutama raw string dengan backtick di Go terasa lebih mulus bagi saya. Di Zig, karena \\, saya malah mengakalinya dengan memakai pendekatan @embedFile

    • Secara visual saya memang tidak terlalu suka \\, tetapi menurut saya itu cara yang rapi untuk menyelesaikan masalah multiline literal dan indentasi. Saya juga tidak tahu bahasa lain yang bisa menyelesaikan masalah ini tanpa bantuan fungsi

  • Sintaks Zig terasa agak ramai. Konstruksi yang diawali @ seperti @TypeOf atau sintaks inisialisasi seperti .{.x} terasa canggung. Mungkin karena saya belum mahir memakai Zig, tetapi secara keseluruhan saya punya kesan bahwa kodenya agak sulit dibaca

    • Saya lebih suka sintaks Odin karena jauh lebih minimal dan lebih rapi. Zig terasa agak ramai

    • . di Zig berperan sebagai placeholder untuk tipe yang diinferensikan. Misalnya Anda bisa menginisialisasi objek seperti ini

      const p = Point{ .x = 123, .y = 234 };
      

      Atau kalau ingin menyatakan inferensi tipe secara eksplisit

      const p: Point = .{ .x = 123, .y = 234 };
      

      Tipe juga bisa dihilangkan pada argumen fungsi sehingga lebih ringkas. Di Rust, tipe harus ditulis secara eksplisit dalam situasi seperti ini

      takePoint(Point{ x: 123, y: 234 });
      

      Dalam inisialisasi struct bertingkat pun, cara inferensi Zig jauh lebih berguna. Di Rust, keharusan menulis tipe secara eksplisit di mana-mana bisa cepat membuat kode terasa ramai. Meski begitu, saya pribadi tetap merasa akan lebih nyaman jika notasi dot di depan dihilangkan, tetapi sepertinya dipertahankan demi menyederhanakan implementasi parser. Notasi x: 123 atau .x = 123 masing-masing dipinjam dari JS dan C99. Secara pribadi saya cukup sering memakai keduanya, jadi tidak terasa aneh

  • Saya jauh lebih menyukai raw string literal C# 11. Indentasi pada baris pertama dijadikan acuan untuk otomatis menyelaraskan indentasi di baris-baris sisanya. Selain itu, kurung kurawal juga bisa dipakai sebagai karakter literal. Jika $ muncul beberapa kali, kurung kurawal diperlakukan sepenuhnya sebagai nilai literal

    string json = $"""
       {title}
    
         Welcome to {sitename}.
    
       """;
    string json = $$"""
       {{title}}
    
         Welcome to {{sitename}}, which uses the {sitename} syntax.
    
       """;
    
    • (Sebagai penulis fitur raw string literal C#) sebenarnya yang dijadikan acuan adalah indentasi pada baris """ terakhir, dan baris pertama juga boleh diberi indentasi. Senang rasanya fitur ini disukai, dan saya cukup bangga karena menurut saya ini fitur yang bagus
  • Sintaks Zig memang bagus, tetapi dibanding Go yang bisa ditulis cukup rapi tanpa titik koma atau :, saya rasa belum sampai level 'lovely'. Kalau harus dibandingkan, memang jauh lebih baik daripada Rust, tetapi Go juga sudah sangat bagus

    • Justru sintaks yang terlalu minimal seperti Go kadang lebih sulit ditafsirkan saat dibaca. Waktu membaca kode lebih banyak daripada menulisnya, jadi keringkasan yang berlebihan justru bisa memicu kesalahan dan membuat debugging lebih sulit. CoffeeScript atau J adalah contoh khas sintaks yang terlalu dipadatkan

    • Saya tidak merasa bahwa menghapus elemen sintaks otomatis membuat sintaks jadi lebih baik. Kalau memang begitu, semua orang pasti akan menulis seperti Lisp, dan teks pun akan ditulis seperti scriptio continua, yaitu gaya tulisan kuno tanpa spasi. Lihat Wikipedia scriptio continua

  • Secara keseluruhan saya puas dengan Zig, tetapi ada beberapa hal yang masih disayangkan

    • Sulit menentukan nilai balik blok. Akan lebih baik jika seperti Rust, ekspresi terakhir otomatis dikenali sebagai nilai kembalian, tetapi di Zig harus memakai label dan semacamnya sehingga terasa merepotkan
    • Chaining pada tipe opsional (misalnya a?.b?.c) tidak dimungkinkan. Kalau ada dukungan tipe monadik, chaining yang lebih umum akan memungkinkan, tetapi untuk saat ini masih kurang
    • Tidak ada dukungan fungsi lambda. Padahal blok fungsi sudah bisa dipakai di tempat seperti loop atau blok catch, jadi rasanya akan lebih fleksibel jika lambda juga didukung
  • Soal penggunaan void sebagai nama tipe, sebenarnya dalam teori tipe, void bukan berperan sebagai 'unit' melainkan berarti tipe 'uninhabited' yang tidak memiliki nilai. Secara tradisional, () atau unit adalah tipe yang memiliki satu anggota. void adalah tipe kembalian untuk fungsi seperti abort

    • Di C dan C++, void sudah digunakan cukup baik dan sangat akrab bagi banyak programmer sistem. Menurut saya, perdebatan istilah dalam teori formal tidak terlalu berarti dalam penggunaan nyata. Banyak orang yang datang ke Zig punya latar belakang C atau C++, jadi void sudah cukup masuk akal

    • abort itu lebih cocok untuk tipe keadaan 'tak terjangkau' seperti tipe ! di Rust. void justru lebih dekat ke unit atau (), yaitu tipe ketika tidak ada nilai yang berarti. Sebagai trik yang menarik, di TypeScript, jika void dipakai dalam generic constraint, parameter tersebut bisa dibuat menjadi opsional

    • Tipe void punya tradisi yang sangat panjang, sampai bisa ditelusuri kembali ke ALGOL 68. Di sana, tipe VOID didefinisikan sebagai tipe yang hanya memiliki satu anggota, yaitu EMPTY

  • Saya kaget mendengar bahwa "Zig tidak punya lambda". Di C++, lambda dipakai hampir di mana-mana, jadi saya penasaran bagaimana comparator untuk hal seperti sort array didefinisikan

    • Karena biasanya perlu mendeklarasikan fungsi secara terpisah, saya merasa bagian itu kurang nyaman di Zig

    • Anda bisa mereferensikan inline struct anonim beserta fungsi di dalamnya. Memang fitur capture yang umum dipakai pada lambda tidak ada di Zig, tetapi itu bisa digantikan dengan meneruskan parameter konteks, biasanya berupa struct

    • Pada dasarnya sama seperti di C: mendeklarasikan fungsi pembanding secara terpisah lalu mengirim pointer-nya ke fungsi pengurutan

  • Orang sering berkata "sintaks tidak penting", tetapi pada praktiknya itu sering berarti "karena sintaks tidak penting, mari pakai gaya yang saya sukai". Saya sendiri juga terbiasa dengan sintaks turunan C seperti Rust/Zig/Go, dan gaya seperti Haskell/OCaml yang membedakan pemanggilan fungsi lewat spasi masih terasa asing sehingga menurut saya menghambat adopsi massal. Seperti keberhasilan Rust, bahasa lain juga bisa belajar dari bagaimana ia berhasil mencampurkan 'bayam' pemrograman fungsional ke dalam 'brownie' bahasa sistem

    • Saya tidak setuju dengan pernyataan bahwa sintaks tidak penting. Pada akhirnya, sintaks adalah antarmuka utama yang dipakai pengguna untuk berinteraksi dengan bahasa. Setiap kali saya membaca suatu bahasa, elemen sintaksnya selalu terasa menonjol secara bawah sadar

    • Jika Anda menginginkan bahasa fungsional dengan sintaks bergaya C, saya merekomendasikan Gleam: gleam.run Kodenya juga sangat cantik

      fn spawn_greeter(i: Int) {
       process.spawn(fn() {
        let n = int.to_string(i)
        io.println("Hello from "  n)
       })
      }
      

      Reason juga layak direkomendasikan. Berbasis OCaml, tetapi memiliki sintaks bergaya C: reasonml.github.io