7 puan yazan GN⁺ 2025-08-22 | 1 yorum | WhatsApp'ta paylaş
  • Zig, Rust'a benzer süslü parantez tabanlı bir sözdizimine dayanır, ancak daha basit dil semantiği ve rafine sözdizimi tercihleriyle bunu geliştirir
  • Tamsayı sabitlerinde tüm türler comptime_int olarak başlar ve atama sırasında açıkça dönüştürülür; dize sabitleri ise \\ tabanlı, kısa ve öz bir ham dize gösterimi kullanır
  • .x = 1 biçimindeki kayıt sabitleri, alan yazımlarını kolay aranabilir hale getirir ve tüm türler önek gösterimiyle tutarlı biçimde ifade edilir
  • and ve or, kontrol akışı anahtar sözcükleri olarak kullanılır; if ve loop yapılarında süslü parantezler isteğe bağlı olarak atlanabilir ve biçimlendirici güvenliği garanti eder
  • Ad alanları olmadan her şey ifade olarak ele alınır; böylece tür, değer ve desen sözdizimi birleşir ve generics, kayıt sabitleri, yerleşik işlevler (@import, @as vb.) kısa ve öz şekilde kullanılabilir

Genel Bakış

  • Zig, Rust'a benzer bir görünüme sahip olsa da daha basit bir dil yapısını benimser
  • Sözdizimi tasarımında grep dostu olma, sözdizimsel tutarlılık ve gereksiz görsel gürültüyü azaltma üzerine odaklanır

Tamsayı Sabitleri

const an_integer = 92;  
assert(@TypeOf(an_integer) == comptime_int);  
  
const x: i32 = 92;  
const y = @as(i32, 92);  
  • Tüm tamsayı sabitleri comptime_int türündedir
  • Bir değişkene atanırken tür açıkça belirtilir ya da @as kullanılarak dönüştürülür
  • var x = 92; biçimi çalışmaz; açık tür gerekir

Dize Sabitleri

const raw =  
    \\Roses are red  
    \\  Violets are blue,  
    \\Sugar is sweet  
    \\  And so are you.  
    \\  
;  
  • Her satır ayrı bir token olduğu için girinti sorunu yoktur
  • \\ ifadesinin kendisini escape etmeye gerek yoktur

Kayıt Sabitleri

const p: Point = .{  
    .x = 1,  
    .y = 2,  
};  
  • .x = 1 biçimi, okuma ve yazmayı ayırt etmede avantaj sağlar
  • .{} gösterimi, bloklardan ayrışırken sonuç türüne otomatik dönüştürülür

Tür Gösterimi

u32        // tamsayı  
[3]u32     // uzunluğu 3 olan dizi  
?[3]u32    // null olabilen dizi  
*const ?[3]u32 // sabit işaretçi  
  • Tüm türler önek (prefix) gösterimi kullanır
  • Dereference işlemi sonek gösterimindedir (ptr.*)

Tanımlayıcılar

const @"a name with space" = 42;  
  • Anahtar sözcük çakışmalarını önlemek veya özel adlar tanımlamak mümkündür

Fonksiyon Bildirimi

pub fn main() void {}  
fn add(x: i32, y: i32) i32 {  
    return x + y;  
}  
  • fn anahtar sözcüğü ile fonksiyon adı bitişik olduğundan arama yapmak kolaydır
  • Dönüş türü gösteriminde -> kullanılmaz

Değişken Bildirimi

const mid = lo + @divFloor(hi - lo, 2);  
var count: u32 = 0;  
  • const ve var kullanılır
  • Tür gösterimi ad: tür sırasındadır

Kontrol Akışı: and/or

while (count > 0 and ascii.isWhitespace(buffer[count - 1])) {  
    count -= 1;  
}  
  • and, or kontrol akışı anahtar sözcükleridir
  • Bit işlemlerinde &, | kullanılır

if İfadesi

.direction = if (prng.boolean()) .ascending else .descending;  
  • Parantez zorunlu, süslü parantez isteğe bağlıdır
  • zig fmt güvenli biçimlendirmeyi garanti eder

Döngüler

for (0..10) |i| {  
    print("{d}\n", .{i});  
} else @panic("loop safety counter exceeded");  
  • Hem for hem while, else bloğunu destekler
  • Yineleyici ve öğe adları sezgisel biçimde yerleştirilir

Ad Alanları ve İsim Çözümleme

const std = @import("std");  
const ArrayList = std.ArrayList;  
  • Değişken shadowing'e izin verilmez
  • Ad alanları ve glob import yoktur

Her Şey Bir İfadedir

const E = enum { a, b };  
const e: if (true) E else void = .a;  
  • Tür, değer ve desen sözdizimi birleşir
  • Tür konumunda koşullu ifade kullanılabilir

Generics

fn ArrayListType(comptime T: type) type {  
    return struct {  
        fn init() void {}  
    };  
}  
  
var xs: ArrayListType(u32) = .init();  
  • Generics, fonksiyon çağrısı sözdizimiyle (Type(T)) ifade edilir
  • Tür argümanları her zaman açıktır

Yerleşik İşlevler

const foo = @import("./foo.zig");  
const num = @as(i32, 92);  
  • Derleyicinin sunduğu işlevler @ önekiyle çağrılır
  • @import, dosya yolunu açıkça gösterir
  • Argüman mutlaka bir dize sabiti olmalıdır

Sonuç

  • Zig sözdizimi, küçük tercihlerin toplamının okuması kolay bir dil ortaya çıkardığı bir örnek
  • Özellik sayısı azaldıkça gerekli sözdizimi de azalır ve sözdizimleri arasındaki çakışma olasılığı da düşer
  • Mevcut dillerin iyi fikirlerini ödünç alırken, gerektiğinde cesurca yeni sözdizimleri de getirir

1 yorum

 
GN⁺ 2025-08-22
Hacker News görüşleri
  • Bu yazı, sözdizimi tasarımında ortaya çıkan çeşitli trade-off’ları derinlemesine ele alıyor ve Zig’in sözdizimindeki minimalizm, tutarlılık ve acımasız denecek kadar okunabilirliğe odaklanması gerçekten etkileyici. Bu, soyut bir güzellik değil; endüstriyel kullanımda sürpriz barındırmayan bir tür "brütalizm" ve hoşuma giden de bu. Böyle dengeli bir sözdizimi tasarımı gerçekten nadir; bence Zig bunu iyi başarmış

    • Makalede hata yönetiminden bahsedilmemesi üzücü. Zig’in try/catch yaklaşımı son derece başarılı; birçok dil arasında en sevdiğim hata yönetimi yöntemi bu. Bu kısmın da anlatılmış olması daha iyi olurdu

    • Zig’in gerçek çekiciliği, "yüzeyde güzel görünen okunabilirlikte" değil; soyutlama yoluyla elde edilen tutarlı güzellikte yatıyor. S-ifadesi ve M-ifadesi benzetmesinde olduğu gibi, genel durumda iyi çalışan bir yaklaşım, birçok istisnai durum için özel tasarlanmış çözümlerden uzun vadede çoğu zaman daha iyidir. C++’ta olduğu gibi sürekli istisna durumları eklenirse sonunda tüm kuralları ezberleme yükü artıyor. Dil tasarımında sadelik ve tutarlılık aranırken karmaşıklığın kullanıcıya yıkıldığı bir "Turing tarpit" tuzağına düşülebilir; bu yüzden özel durumların genel kurallardan doğal biçimde çıkması önemli. XKCD’nin New Pet çiziminde de buna benzer bir örnek var

    • Özellikle etkileyici bulduğunuz örnekleri paylaşabilir misiniz diye merak ettim

  • Zig’in Rust gibi isim:tip biçiminde tür belirtmesi kullanmasına gelince, açıkçası türün önce geldiği geleneksel yaklaşımı daha çok seviyorum. Bir değişkenin bildirimine tekrar baktığımda en çok merak ettiğim şey o değişkenin türü oluyor; bunu hızlıca bulamamak rahatsız edici. Özellikle Rust’ta let mut gibi gereksiz tekrarlar fazlasıyla var ve bu da işleri zorlaştırıyor; C ve C++’ta olduğu gibi türün önce gelmesi daha hoş. Pratikte ideal olanın, tür çıkarımını sadece gerçekten gerekli yerlerde asgari düzeyde kullanmak olduğunu düşünüyorum

    • let anahtar sözcüğünün gerçekten bir bildirim ifadesi olduğunu açıkça göstermesi bakımından gerekli olduğu taraflar da var. Aksi halde C++’taki belirsiz sözdizimi ayrıştırma sorunları yaşanabilir

    • Ben de her zaman önce değişkenin türüne bakmaya çalıştığım için türün önde olduğu biçimi tercih ediyorum. Ayrıştırıcı açısından önce adı işlemek daha kullanışlı ve TypeScript’in bunu JavaScript uyumluluğu nedeniyle seçtiğini anlayabiliyorum. Sonuçta önemli olanın kullanımı kolay bir standart kütüphane olduğunu düşünüyorum. Tür sistemini aşırı zorlayan örneklerde olduğu gibi her durumu ille türlerle ifade etmeye çalışmak yerine niyeti açıkça aktarmak daha önemli

    • Kodda bir değişkenin türünü görmek için yukarı çıkıyorum ama ironik biçimde, tür önce gelince aradığım değişken bildirimini bulmak daha zor oluyor. Tür adının en başta olması ve uzunluğunun değişkenlik göstermesi yüzünden gözümü sağa sola sürekli oynatmam gerekiyor; bu da verimsiz hissettiriyor

    • Çoğu durumda editörde üzerine gelince tür bilgisi hemen gösterildiği için, kodda türün konumu o kadar önemli olmayabilir. Rust’ın ayrıntılı görünmesinin önemli bir nedeni, ayrıştırma belirsizliğini önlemeye yönelik uygulama tarafı kaygılar. C ve C++’ta olduğu gibi tür önce gelince, belirli bir adla tanımlanmış değişkenleri grep ile kolayca bulmak zorlaşıyor; dönüş türünü başa koyma stili şablonlar nedeniyle ortaya çıkmış olsa da bazı durumlarda kodu okumayı ve aramayı kolaylaştırabiliyor

    • Ben şahsen Pascal tarzı tür belirtimini daha çok seviyorum. Tür çıkarımı yapılırken ayrıca auto gibi dolaylı bir özelliğe gerek bırakmıyor ve ayrıştırma açısından da daha az belirsiz. MyClass x ifadesinde MyClass’ın tür mü yoksa değişken adı mı olduğu hemen anlaşılmıyor; bu yaklaşım bu tür belirsizlikleri azaltıyor

  • Zig’in raw/multiline string sözdiziminde \\ karakterini tekrar tekrar yazmak zorunda olmak fazla kafa karıştırıcı ve aşırı geliyor

    • Python, C++, Rust gibi dillerde çok satırlı string biçimlendirdiyseniz bunun zorluğunu anlarsınız. Girintinin string içeriğine dahil olması sürekli sorun yaratıyor ve YAML gibi girinti kaldırma modları olan çözümler ise bazen daha da kafa karıştırıcı olabiliyor. Zig’in yaklaşımı en azından girinti konusunda son derece net

    • Başta bu sözdizimi bana çok rahatsız edici gelmişti ama Zig kullandıkça insan alışıyor ve hatta avantajlarını görmeye başlıyor. Zig, ilk karşılaşmada itici gelebilen ama kullandıkça değerini gösteren türden bir dil

    • Aslında bu çılgın bir sözdizimi değil; çılgın olan, bu karmaşık problem: çok satırlı bir string’in içine başka bir çok satırlı string’i güvenli biçimde koymak. Zig’de ayrıca escape gerekmemesi ve girinti derdi olmaması güzel

    • Kotlin’in trimIndent, Go ve Java’nın text block yaklaşımı ve özellikle Go’nun backtick raw string biçimi bana daha pürüzsüz geliyor. Zig’de \\ yüzünden bazen dolaylı olarak @embedFile kullanıyorum

    • Görsel olarak \\ hoşuma gitmiyor ama çok satırlı literal ve girinti problemini temiz biçimde çözen bir yöntem olduğunu düşünüyorum. Bunu fonksiyon kullanmadan çözen başka bir dil aklıma gelmiyor

  • Zig’in sözdizimi dağınık hissettiriyor. @TypeOf gibi @ ile başlayan yapılar ya da .{.x} gibi başlatma sözdizimi bana tuhaf geliyor. Zig kullanımında çok deneyimli olmadığım için de olabilir ama genel olarak kodu okumak zor geliyor

    • Odin’in sözdizimi çok daha minimal ve iyi cilalanmış olduğu için onu tercih ediyorum. Zig biraz daha dağınık hissettiriyor

    • . Zig’de çıkarılan tür için bir placeholder görevi görüyor. Örneğin bir nesneyi şöyle başlatabilirsiniz

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

      Ya da tür çıkarımını açıkça belirtmek isterseniz

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

      Fonksiyon argümanlarında da türü atlayabildiğiniz için daha kısa oluyor. Rust’ta bu gibi durumlarda türü açıkça yazmanız gerekir

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

      İç içe geçmiş struct başlatmalarında da Zig’in çıkarım yaklaşımı çok daha kullanışlı. Rust’ta her yerde türü açıkça yazmak gerektiği için kod hızla dağınıklaşabiliyor. Yine de öndeki nokta gösteriminin kaldırılmasının daha kullanışlı olacağını düşünüyorum ama muhtemelen ayrıştırıcıyı basitleştirmek için korunuyor. x: 123 ya da .x = 123 gösterimleri sırasıyla JS ve C99’dan alınmış. Ben şahsen ikisini de sık kullandığım için tuhaf gelmiyor

  • C# 11’in raw string literal yaklaşımını çok daha fazla tercih ediyorum. İlk satırın girintisini referans alıp kalan satırlarda girintiyi otomatik hizalıyor. Ayrıca süslü parantezleri de düz karakter olarak kullanabiliyorsunuz. $ birden fazla kez görünürse süslü parantezleri tamamen değer olarak ele alıyor

    string json = $"""
       {title}
    
         Welcome to {sitename}.
    
       """;
    string json = $$"""
       {{title}}
    
         Welcome to {{sitename}}, which uses the {sitename} syntax.
    
       """;
    
    • (C# raw string literal özelliğinin yazarı olarak) Aslında referans alınan şey son """ satırının girintisi ve ilk satırın da girintili olmasına izin veriliyor. Bu özelliği sevmenize sevindim; bence de iyi bir özellik
  • Zig’in sözdizimi güzel ama Go gibi noktalı virgül ya da : olmadan da yeterince temiz yazılabildiği düşünülürse, buna "lovely" diyecek kadar ileri gitmem. Karşılaştırmak gerekirse Rust’tan epey daha iyi olduğu doğru ama Go da gayet başarılı

    • Tam tersine, Go’daki gibi aşırı minimalist sözdizimleri bazen okurken yorumlamayı zorlaştırabiliyor. Kodu yazmaktan çok okuduğumuz için, gereğinden fazla kısalık hata riskini artırıp debug etmeyi zorlaştırabiliyor. CoffeeScript ve J gibi aşırı sıkıştırılmış sözdizimleri bunun iyi örnekleri

    • Sözdizimsel öğeleri kaldırmak tek başına daha iyi sözdizimi demek değil. Öyle olsaydı herkes Lisp gibi yazardı ve metinler de scriptio continua gibi boşluksuz yazılırdı. Bkz. scriptio continua Wikipedia

  • Zig’den genel olarak memnunum ama şu eksikler can sıkıyor

    • Blokların dönüş değerini belirtmek zor. Rust’taki gibi son ifadenin otomatik dönüş sayılması güzel olurdu ama Zig’de label gibi şeyler kullanmak gerektiği için zahmetli
    • Optional type chaining (a?.b?.c gibi) mümkün değil. Monadik tür desteği olsa daha genel zincirleme mümkün olurdu ama şu an eksik
    • Lambda fonksiyonu desteği yok. Döngülerde ya da catch bloklarında zaten işlevsel bloklar kullanılıyor; lambda da olsa daha esnek olurdu
  • Tür adlandırmasında void kullanılmasına gelince, aslında tür kuramında void, unit rolünü değil, değer içermeyen uninhabited türü ifade eder. Geleneksel olarak () ya da unit, tek üyesi olan türdür. void, abort gibi fonksiyonların dönüş türüdür

    • C ve C++’ta void bu şekilde gayet yerleşik kullanılıyor; bu yüzden birçok sistem programcısına tanıdık geliyor. Biçimsel tür kuramı terminolojisi üzerindeki tartışmaların pratik kullanım açısından anlamsız olduğunu düşünüyorum. Zig’e gelen birçok kişinin C/C++ geçmişi olduğu için void gayet uygun

    • abort, Rust’taki ! tipi gibi, "ulaşılamaz" durumları temsil eden türdür. void ise daha çok unit ya da ()’a yakındır; yani değeri olmayan değil, anlamlı içerik taşımayan türdür. Eğlenceli bir ayrıntı olarak, TypeScript’te void bir generic kısıt olarak kullanılırsa ilgili parametre opsiyonel hâle getirilebilir

    • void tipi çok eski bir geleneğe sahip; kökeni ALGOL 68’e kadar gidiyor. Orada VOID tipi tek üyeli (EMPTY) bir tür olarak tanımlanıyor

  • "Zig’de lambda yok" denmesi şaşırtıcı. C++’ta lambda’ları neredeyse her yerde kullanıyorum; o zaman dizi sıralama gibi yerlerde comparator nasıl tanımlanıyor merak ettim

    • Genelde ayrı bir fonksiyon bildirimi yapılıyor olması yüzünden bu kısmın Zig’de kullanışsız olduğunu düşünüyorum

    • Anonim struct ve içindeki fonksiyonlara satır içinde referans vermek mümkün. Aslında Zig’de lambda’larda sık kullanılan capture özelliği yok ama bunun yerine context parametresi (genellikle bir struct) geçirilebiliyor

    • Temelde C ile aynı: ayrı bir karşılaştırma fonksiyonu tanımlayıp onun pointer’ını sıralama fonksiyonuna veriyorsunuz

  • İnsanlar "sözdizimi önemli değil" diyor ama pratikte kastettikleri şey çoğu zaman "sözdizimi önemli değil, o yüzden benim tercih ettiğim gibi yazalım" oluyor. Ben de Rust/Zig/Go gibi C ailesinden türemiş sözdizimlerine alışığım ve Haskell/OCaml’daki gibi fonksiyon çağrılarını boşlukla ayıran tarz bana hâlâ yabancı; bunun yaygınlaşmanın önünde bir engel olduğunu düşünüyorum. Rust’ın başarısında olduğu gibi, fonksiyonel programlamanın "ıspanağını" sistem dili "brownie"sinin içine iyi yedirmiş olması, başka dillere de örnek olabilir

    • Sözdiziminin önemli olmadığını söyleyen görüşe katılmıyorum. Sonuçta sözdizimi, kullanıcının dille etkileşime geçtiği ana arayüz. Bir dili her okuyuşumda sözdizimsel unsurlar bilinçdışımda daha belirgin biçimde öne çıkıyor

    • C tarzı sözdizime sahip bir fonksiyonel dil istiyorsanız Gleam’i öneririm: gleam.run Kodu da çok hoş görünüyor

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

      Reason da önerilebilir. OCaml tabanlı ama C tarzı sözdizimine sahip: reasonml.github.io