- 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
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/catchyaklaşı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 olurduZig’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:tipbiç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’talet mutgibi 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üyorumletanahtar 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şanabilirBen 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
grepile 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ırabiliyorBen şahsen Pascal tarzı tür belirtimini daha çok seviyorum. Tür çıkarımı yapılırken ayrıca
autogibi dolaylı bir özelliğe gerek bırakmıyor ve ayrıştırma açısından da daha az belirsiz.MyClass xifadesinde 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ıyorZig’in raw/multiline string sözdiziminde
\\karakterini tekrar tekrar yazmak zorunda olmak fazla kafa karıştırıcı ve aşırı geliyorPython, 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@embedFilekullanıyorumGö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 gelmiyorZig’in sözdizimi dağınık hissettiriyor.
@TypeOfgibi@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 geliyorOdin’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şlatabilirsinizYa da tür çıkarımını açıkça belirtmek isterseniz
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
İç 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: 123ya da.x = 123gösterimleri sırasıyla JS ve C99’dan alınmış. Ben şahsen ikisini de sık kullandığım için tuhaf gelmiyorC# 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"""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 özellikZig’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
a?.b?.cgibi) mümkün değil. Monadik tür desteği olsa daha genel zincirleme mümkün olurdu ama şu an eksikcatchbloklarında zaten işlevsel bloklar kullanılıyor; lambda da olsa daha esnek olurduTür adlandırmasında
voidkullanılmasına gelince, aslında tür kuramındavoid,unitrolünü değil, değer içermeyenuninhabitedtürü ifade eder. Geleneksel olarak()ya daunit, tek üyesi olan türdür.void,abortgibi fonksiyonların dönüş türüdürC ve C++’ta
voidbu ş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çinvoidgayet uygunabort, Rust’taki!tipi gibi, "ulaşılamaz" durumları temsil eden türdür.voidise daha çokunitya 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’tevoidbir generic kısıt olarak kullanılırsa ilgili parametre opsiyonel hâle getirilebilirvoidtipi çok eski bir geleneğe sahip; kökeni ALGOL 68’e kadar gidiyor. OradaVOIDtipi 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
Reason da önerilebilir. OCaml tabanlı ama C tarzı sözdizimine sahip: reasonml.github.io