- C kodu derleme ve çapraz derleme özelliklerini varsayılan olarak sunan Zig derleyicisi, 45 yıllık deneyime sahip yazarın karşılaştığı diller arasında en şaşırtıcı olanı
- Derleme zamanında yürütme, keyfi bit genişliğinde değişkenler, test blok ortamı gibi özgün özellikleriyle basit bir C/C++ alternatifi olmanın ötesine geçip tamamen yeni bir programlama yaklaşımı sunuyor
- Tip çıkarımıyla değişken tanımlama, anonim yapılar, etiketli
break gibi kısa ve net sözdizimi sayesinde hızlı öğrenilebiliyor
- Test bloklarıyla bağımsız modül testi ve
@breakpoint yerleşik fonksiyonu sayesinde optimize edilmiş kodun hata ayıklanmasını destekliyor
- Bit alanları ve bit işlemleriyle düşük seviyeli programlamayı destekleyerek verimlilik ve sağlamlığı aynı anda sağlıyor; yorumlanan dillerin avantajlarını derlenen dillere entegre ediyor
Önsöz
- 45 yıllık kariyeri boyunca Zig kadar şaşırtıcı bir dil olmamış
- Zig yalnızca yeni bir dil değil, programlama biçimini kökten değiştiren bir araç
- Onu yalnızca C ya da C++ yerine geçecek bir dil olarak görmek büyük bir küçümseme olur
- Bu yazının amacı, Zig’in basit ama çekici özelliklerini tanıtmak ve programcıların hızlıca başlamasına yardımcı olmak
- Sektörde Zig’in kabulünü etkileyen daha birçok özellik mevcut
Zig derleyicisi
- C kodu derleme ve çapraz derleme yeteneklerini ek ayar gerektirmeden varsayılan olarak sunması, sektör açısından büyük etki yaratıyor
- Kurulum için Ziglang indirme sayfasından işlemci/OS’e uygun derleyici indirilip arşiv açıldıktan sonra istenen dizine kopyalanıyor
- Windows 10’da x86_64 zip dosyası
Program Files içine kopyalanıp kök dizin adı zig-windows-x86_64 olarak değiştirilirse, sürüm güncellemelerinde Path ortam değişkenini yeniden düzenlemek gerekmez
- Kök dizin yolu Path ortam değişkenine eklendikten sonra derleyici CLI modunda kullanılabilir
"Hello World!" programını derlemek için resmî sitedeki Getting Started bölümüne bakılması öneriliyor
Temel kavramlar ve komutlar
Değişken tanımlama
- Değişken tanımı; erişilebilirlik (
pub veya boş), var/const, değişken adı içeren birinci bölüm, tip bildiren ikinci bölüm ve ilklendirmeyi yapan üçüncü bölümden oluşur
- Yalnızca birinci ve üçüncü bölümler zorunludur; tip, ilklendirme değerinden çıkarılabilir
- Örnek:
var sum : usize = 0;
pub olmadan tanımlanan değişkenlere yalnızca modül içinden erişilebilir (C’deki static değişkenlere benzer)
pub değişken tanımı önerilmez; bağlılığı azaltıp iç uyumu artırmak için pub fonksiyonların da en aza indirilmesi tavsiye edilir
Yapılar, anonim yapılar, test blokları
.{ ve } ile çevrili anonim yapı literalleri, başka yapı öğelerini ilklendirmek ya da öğeleri ilklendirilmiş yeni bir yapı oluşturmak için kullanılır
.{ } boş anonim yapı literalidir
struct { } biçimi yapı tanımıdır
- Test blokları, çalıştırılabilir dosya olmadan derleme ve test yürütmeye olanak tanır
Bit alanları
- Bit alanları,
packed struct içinde belirli boyutta tipe sahip alanlar olarak tanımlanır
- İşaretçiler belirli bir bit alanını gösterebilir
For döngüsü
- Zig sözdizimi C’den daha açıktır, ancak
[0..8] yerine açık aralık [0..9) kullanır
- Döngü değişkeni
i için tip tanımı, ilklendirme, test ve artırım otomatik olarak yapılır
Diziler
[_], boyutu bilinmeyen bir diziyi tanımlar; ardından öğe tipi ve ilklendirme gelir
- Örnek:
var grid = [_]u8{0} ** 81; ifadesi, 81 adet u8 öğeyi 0 ile ilklendirir
- Dizi boyutu, ilklendirme tekrar argümanından çıkarılır
- Test ortamında dizi öğeleri dolaşılıp toplanabilir
for döngüsünde | işaretleri arasında tanımlanan değişkenin, dizi öğesiyle aynı tipte olduğu otomatik kabul edilir
usize, platformun doğal işaretsiz tamsayısıdır (64 bitte u64, 32 bitte u32)
Çok öğeli işaretçiler
- Bir dizi işaretçisinin işaretçi aritmetiği kullanabilmesi için
[*]const i32 gibi açıkça çok öğeli işaretçi olarak tanımlanması gerekir
- Dizi
const olsa da işaretçi var olarak tanımlanabilir
İşaretçi çözümleme
- Dizide tek bir konumun adresini tutan işaretçi, işaretçi aritmetiğiyle güncellenemez
- İşaretçi çözümleme
ptr.* ile yapılır
Etiketli break
- Derleme zamanında dizi ilklendirme gibi çeşitli işler yapılabilir
- Etiketli
break, blok adının sonuna : eklenerek tanımlanır ve break ile bloktan değer döndürülür
0.., 0’dan başlayan sonsuz aralıktır
for döngüsünde değişkenler otomatik ilklendirilir ve artırılır; dizinin son konumu işlendikten sonra döngü biter
- Diziler açıkça
undefined ile ilklendirilmek zorunda değildir
Zig’de fonksiyonlar
- Fonksiyonlar
fn ile tanımlanır ve varsayılan olarak static’tir (yalnızca dosya içinde kullanılır)
pub fn ile tanımlanırsa başka dosyalardan import edilebilir
- Fonksiyonlar
inlined olabilir
- Fonksiyon işaretçisinde
const önce gelir, ardından fonksiyon prototipi yazılır
Zig’de nesne yönelimli programlama
- Yapılar fonksiyon içerebilir
- Yığın örneğinde en fazla 81 öğe (
StkNode tipi) saklanabilir
++ ve -- işleçleri Zig’de yoktur; bunun yerine += ve -= kullanılır
- Yığın işaretçisi,
stk dizisinin indeksi olarak kullanılan bir tamsayıdır
self işaretçisi parametre olarak açıkça geçirilmez; fonksiyonun çağrıldığı yığın örneğinin işaretçisi olarak dolaylı biçimde varsayılır
stack.pop() çağrısında self, stack için bir işaretçidir (Java/C++ içindeki this gibi)
init() fonksiyonu yığın yapıcısıdır
pop ve push fonksiyonları inlined durumdadır
Zig programını derleme ve çalıştırma
Çalıştırılabilir dosya oluşturma
- Çalıştırılabilir dosya üretmek için program giriş noktasını gösteren bir
main fonksiyonu gerekir
- Basit programlarda
main fonksiyonu aynı dosyada bulunabilir
- Modülü bağımsız hata ayıklamak için dosyanın sonuna
main fonksiyonu eklenip, iş bittikten sonra yorum satırına alınabilir
- Derleme komutu:
zig build-exe -O ReleaseFast program.zig
Modülün test bloklarını çalıştırma
- Bu, Zig’in en iyi özelliklerinden biridir ve test ile prototipleme için kullanılır
- Test bloğu
test "message" { ile başlar ve } ile biter
"message", test çalışırken gösterilecek metindir
- Test blokları çalıştırılabilir dosyadan bağımsız olarak yürür; nihai çalıştırılabilir dosya testleri çalıştırmaz
- Test komutu:
zig test module.zig
example.zig içindeki test bloğu, set ve print fonksiyonlarını test eder; set ondalık sayı dizgesini parametre olarak alır, print ise önce Input Grid başlığını yazdırıp ardından grid içeriğini gösterir
Zig’de çıktı
std.debug.print ifadesi, standart Zig kütüphanesi std içindeki debug.zig dosyasında yer alan print fonksiyonunu çağırır
- İlk parametre biçim dizgesidir, ikinci parametre ise gösterilecek değişken listesini içeren anonim yapıdır
- Biçim yoksa yapı boştur
- Varsayılan olarak
stderr üzerine yazdırır
- C’deki
printften farklı olarak Zig, literal dizgeleri ve değişken listesini derleme zamanında işleyebilir
Çalıştırılabilir dosyada hata ayıklama
- Entegre hata ayıklayıcıya sahip bir IDE (
Eclipse, IntelliJ IDEA) veya tümleşik geliştirme kiti (w64devkit) dışında hata ayıklayıcı kullanmak kolay değildir
- Sembol entegrasyonu kodu şişirir ve
Debug modunda derleme gerektirerek verimliliği belirgin biçimde düşüren çalıştırılabilir kod üretir
- Zig, bu sorunları önlemek için pratik bir çözüm sunar
@breakpoint yerleşik fonksiyonu
- Kaynak koda
@breakpoint(); eklenirse, program hata ayıklayıcı altında çalışırken bu noktada durur
- Semboller olmadan optimize edilmiş Zig kodunda hata ayıklamayı mümkün kılan yararlı bir özelliktir
@breakpoint(); satırından hemen önce std.debug.print ile izlenecek değişkenler yazdırılırsa, o andaki değişken değerleri görülebilir
debug_example.zig örneğinde set fonksiyonu içine grid ve değişkenleri yazdıran kod ile @breakpoint(); eklenmiştir
- Derleme komutu:
zig build-exe debug_example.zig
gdb gibi bir hata ayıklayıcıyla debug_example.exe çağrıldıktan sonra r komutuyla program başlatılır
c komutuyla devam edilerek grid içeriği ve değişkenler izlenir
- Enter tuşuna art arda basılarak devam edildiğinde,
grid değerlerinin example.zig test bloğundakilerle eşleştiği görülebilir
Zig’de düşük seviyeli programlama
Matris gösterimi
- Ondalık rakamlar, matris içinde standart
u8 tamsayıları olarak saklanır
- Girdi
gridi dizge biçiminde olsa da ASCII karakterler dahili olarak u8 tamsayılarına dönüştürülür
- Sayılar, 81 konumlu
grid dizisinde satır satır doğrusal biçimde saklanır: var grid = [_]u8{0} ** 81;
grid doğruluğunu doğrulamak için öğelere her satır ve sütun üzerinden erişmek gerekir
- 9 işaretçiden oluşan bir dizi oluşturulur; her işaretçi ilgili satırın başlangıcını gösterir
- Etiketli
break ile bir kod bloğundan değer döndürülebilir: break :fill9x9 m; ifadesi, matrixi m ile ilklendirir
- Öğe erişim gösterimi:
element = matrix[i][j]
Ondalık rakamları bitlerle göstermek
- Tamsayı ondalık rakam
i değerini tamsayı code ile değiştiren temel fikir
i ∈ [1,9] → code = 2ⁱ⁻¹
i = 0 → code = 0
code içindeki tek 1 biti, i 1 ile 9 arasındaysa i-1 konumundadır; aksi durumda tüm bitler 0’dır
- Her rakam için
code değerlerini veren bir tablo sunuluyor (1→1, 2→2, 3→4, ..., 9→256)
Zig’de code hesaplama
c sıfır değilse code değeri sola kaydırma işleciyle hesaplanır: code = @as(u9,1) << (c-1);
- Zig’de sabitlerin, işlemin derlenip sonucun değişkene atanabilmesi için uygun boyutta olması gerekir
code, u9 tipi olarak tanımlanır (en büyük değer 256 olduğu için en az 9 bit gerekir)
- Zig keyfi bit genişliğinde değişkenler barındırabilir
- Yerleşik
@as fonksiyonuyla 1 sabiti u9 tipine dönüştürülür
Bit alanları kullanarak grid gösterimi
Satır bazlı bit alanı gridi
lines dizisi, her satırı 9 bitlik tamsayı olarak gösterip tüm gridi yansıtır: var lines = [_]u9{0} ** 9;
i satırıyla diziye erişildiğinde, belirli bir sayının o satırda zaten bulunup bulunmadığı bit düzeyinde VE (&) işlemiyle kontrol edilir: lines[i] & code
- Sonuç 0 ise sayı satır
i içinde henüz yoktur; aksi hâlde tekrar vardır
Sütun bazlı bit alanı gridi
columns dizisi, her sütunu 9 bitlik tamsayı olarak gösterip tüm gridi yansıtır: var columns = [_]u9{0} ** 9;
j sütunuyla diziye erişildiğinde, belirli bir sayının o sütunda zaten bulunup bulunmadığı bit düzeyinde VE işlemiyle kontrol edilir: columns[j] & code
- Sonuç 0 ise sayı sütun
j içinde henüz yoktur; aksi hâlde tekrar vardır
Sudoku kuralları
- Boş bir Sudoku
gridine yeni bir sayı eklenirken, bu sayı ilgili satırda, sütunda ve hücrede zaten bulunmamalıdır
- Hücreler, kalın çizgilerle ayrılan 9 adet 3x3
griddir
- 9x9
grid içindeki her öğe, kendisini içeren benzersiz bir satır, sütun ve hücreye sahiptir
- Örnek
gridde ilk hücre 3, 5, 6, 8, 9 sayılarını içerir; 1, 2, 4, 7 eksiktir
lines ve columns dizileri satır ve sütun tekrarı denetimini yapar
- Hücre tekrarlarını denetlemek için yeni bir dizi gerekir
Hücre bazlı bit alanı gridi
cells dizisi, her hücreyi 9 bitlik tamsayı olarak gösterip tüm gridi yansıtır: var cells = [_]u9{0} ** 9;
cells dizisine 3x3 matris olarak erişmek daha kolaydır
- 9x9 matris için yapıldığına benzer biçimde
cell dizisi doldurulur
- Orijinal 9x9
grid öğesinin satır ve sütunundan cell matrisinin satır ve sütunu belirlenmelidir
- Tamsayı bölme çok yavaş olduğundan, bölme sonucunu vermek için
cindx = [_]usize{ 0,0,0, 1,1,1, 2,2,2 }; dizisi kullanılır
- 9x9
grid içindeki bir öğenin satırı i ve sütunu j ile matrise erişildiğinde, ilgili hücrede belirli bir sayının zaten bulunup bulunmadığı bit düzeyinde VE işlemiyle kontrol edilir: cell[cindx[i]][cindx[j]] & code
- Sonuç 0 ise sayı hücrede henüz yoktur; aksi hâlde tekrar vardır
Öğe tekrarını test etme
- Aynı satır, sütun ve hücredeki önceki tüm öğeler bit düzeyinde VEYA (
|) ile birleştirilir, ardından öğenin code değeriyle bit düzeyinde VE işlemi yapılarak tekrar kontrolü tamamlanır
if (((lines[i]|columns[j]|cell[cindx[i]][cindx[j]])&code) != 0) {
unreachable;
}
- Sonuç 0 ise öğe henüz satırda, sütunda veya hücrede yoktur
- Sonuç 0 değilse program
unreachable komutunu çalıştırarak durur
- Bu, Zig’de çalışma zamanı hatasını açıkça göstermenin en basit yoludur
- Gerçek kod hata oluşan yerin ayrıntılarını da yazdırır
- Örnek: giriş dizgesindeki ilk
8ten hemen sonraki 0, 5 ile değiştirilirse hata oluşur; çünkü 3. satır 1. sütunda zaten bir 5 vardır
Veri yapılarını güncelleme
set fonksiyonunda iç içe iki for döngüsü, girdi dizgesi s içindeki her yeni öğeyi satır satır gride kopyalamak için birlikte çalışır
k değişkeni, s dizgesindeki yeni giriş karakterinin indeksini tutar
- Karakterden
'0' çıkarılarak u4 tipinde (c değişkeni) dönüştürme yapılır
gride eklenecek yeni öğe 0 değilse (c != 0), sola kaydırma komutuyla hesaplanan code, her yansıtılmış gride kopyalanır
- İlgili yansıtılmış
grid üzerinde bit düzeyinde VEYA (|=) yapılır:
lines[i] |= code;
columns[j] |= code;
cell[cindx[i]][cindx[j]] |= code;
c değerinin 1 ile 9 arasında olup olmadığını açıkça test etmek gerekmez; çünkü aksi durumda kaydırma işlemi sırasında taşma oluşur
- Örnek: giriş dizgesindeki ilk
8ten hemen sonraki 0, : ile değiştirilirse çalışma zamanı hatası oluşur
- Aynı
0, / ile değiştirilirse de benzer bir çalışma zamanı hatası oluşur
- Program yalnızca değerler 1 ile 9 arasındaysa, yani girdi
gridi yalnızca ondalık rakamlar içeriyorsa doğru çalışır
- İnternetteki birçok Sudoku
gridi 0 yerine . kullanır; bu yüzden set fonksiyonunda if (s[k] == '.') c = 0; satırı bulunur
- Bu sayede
c değeri 0 olur ve kaydırma işlemi pratik biçimde atlanır
Prototipleme ve sağlamlık
- Önceki iki bölümdeki zorunlu hatalar, Zig’in önemli bir özelliğini gösterir
- Bunlardan biri Zig’in sağlamlığıdır: kaydırma işleminde hatalı davranışa izin verilmez ve sorun çalışma zamanında yakalanır
- Her şey verimlilik için yapılmış gibi görünse de, bu performansın sağlamlıkla takas edildiği tipik bir örnektir
- C’de kaydırma işlemi bit kaybederse bu programcının sorunudur; bunun karşılığında belirli assembly komutlarında daha iyi performans elde edilir
- Bir diğer özellik ise test bloklarını prototipleme için kullanabilme olanağıdır
- Uygulama alanları sayısızdır; burada gösterilen örnek yalnızca hata durumunda belirli bir senaryonun nasıl ayıklanabildiğidir
- Yalnızca bu özellikler bile, özellikle derlenen programlama dillerinde çok ender görülen şaşırtıcı yetenekler sunar
Sonuç
- Zig, C uyumluluğu, çapraz derleme, kolay kurulum olmak üzere üç temel unsurdan oluşuyor
- Bu özellikler, onun sistem programlama dilleri için yeni standart olma potansiyeline işaret ediyor
- Yalnızca yorumlanan dillerde görülen birçok avantaj, daha iyi performans sağlamak için giderek derlenen dillere taşınıyor
- Zig’de derleme zamanında yürütme kavramı sayesinde yorumlanan dillerle benzerlik çok belirgin
- Bu da Zig’i hem özel olarak farklı ve güçlü kılıyor hem de anlamayı zorlaştırabiliyor
1 yorum
Hacker News görüşleri
Bu yazı başlangıçta “Zig basit bir dil değil, tamamen yeni bir programlama biçimi” iddiasında bulunuyor, ama gerçekte Zig’e özgü özelliklere neredeyse hiç değinmiyor
Tip çıkarımı, anonim struct'lar, labeled break gibi şeyler zaten çok uzun zamandır başka dillerde de vardı
Gerçekten benzersiz olan şey comptime, ama bu kısım hiç anılmıyor
Lisp makroları gibi tamamen yeni bir kavram değil, ama Zig’in bunu generics yerine kullanma biçimi ilginç
Yine de yazının iddiası oldukça abartılı hissettiriyor
Rust, kodun hangi anda çalıştığını açıkça ifade edebiliyor ve tüm kod alanını dolaşan sorgu motoru benzeri tasarımı etkileyici
D dokümantasyonu bağlantısı bakınız
const-expression ise otomatik olarak çalıştırılıyor
Çünkü Java/Scala kadar birbirinden farklı diller haline geldiler
Zig, C++ template'lerinden daha temiz ama devrimsel olmaktan ziyade pratik bir alternatif gibi duruyor
Kişisel olarak Rust dönemindeki aşırı heyecanı anladığım kadar bunu da pek anlayamıyorum
Zig belgelerini baştan sona okudum ama şaşırtıcı bir şey bulamayınca afalladım
Zig’in en büyük sorunu, hatalara veri eklenememesi
Hatalar sadece yan kanal üzerinden iletiliyor, bu da debug etmeyi zorlaştırıyor ve sonuçta geliştiriciler hata verisini atlamaya başlıyor
ilgili issue bakınız
AccessDenied gibi basit kodlarla asıl nedenin ne olduğunu anlamak zor
Gerçekte karmaşık
Errornesneleri kullansanız bile çoğu zaman ayrı bir teşhis kanalı gerekiyorPerformans ek yükü ya da sistem durumu sorunları nedeniyle, duruma göre bunu geç bağlamayla ele almak daha güvenli olabilir
Zig, bu tür hassasiyet ve determinizm önceliğini benimseyen bir felsefeye sahip
ilgili issue bakınız
Ama gerçekten gereken şey yapısal loglama ve çağrı yığını temelli bağlam takibi
Tembel geliştiricilerin her yere gelişigüzel veri yapıştırmasını engelleyebiliyor
“Zig geliştirme biçiminin kendisi yeni bir dil geliştirme biçimi” iddiasına katılıyorum
Özellikleri dikkatle değerlendirip gereksiz olanları ayıklayan yavaş evrim süreci etkileyici
Zig’e özgü olan şeyin tam olarak ne olduğunu daha somut duymak isterim
Zig’i PyPI üzerinden kurabilmek hoşuma gidiyor
ziglang paketi
pip install ziglangile kurulabiliyor ve hemen kullanılabiliyoruvxile C kodu derlemek de mümkünAda, Object Pascal, Modula-2 gibi dillerde zaten bulunan özelliklerin Zig’in “yeniliği” gibi paketlenmesi hayal kırıklığı yaratıyor
C tarzı sözdizimiyle yeniden sunulunca 40 yıllık fikirlerin yeni görünmesi ilginç
Yazının giriş kısmı iyiydi ama sonrasında sadece Zig özelliklerinin listelenmesine dönüştü
Zig’in sezgisel sözdizimi ve açık kontrol akışı (
defervb.) çekicicomptime sayesinde ayrıca bir makro sözdizimi öğrenmek gerekmiyor
Tüm parçalar doğal biçimde birbirine oturuyor; ilk kez kullansanız bile sanki uzun zamandır kullandığınız bir araçmış gibi hissettiriyor
Zig’in
for (0..9)sözdizimi sezgisel, ama açık aralık olduğu için bazen kafa karıştırıyorPython’daki range(0, 9) gibi, son değerin dahil olup olmadığını unutmak kolay
0..9ve0..=9diye ayırarak daha açık hale getiriyorAralık boyutu basitçe fark alınarak hesaplanıyor ve ters yönde dolaşmak da kolaylaşıyor
0..<5(açık) ve0...5(kapalı) ile bunu daha açık biçimde ayırıyorZig’in tanımlayıcı kuralları hoşuma gitmiyor
snake_case ile camelCase’in karışık kullanılması tuhaf hissettiriyor
Yine de build sistemi, allocator'lar ve derleme deneyimi çok başarılı
Esas olarak Rust kullanıyorum ama Zig’e karşı merakım sürüyor
C kütüphanelerindeki önek kuralları da aynı şekilde can sıkıcı geliyor
Zig’in çekiciliği tek bir özellikte değil, pratik kararların birikiminde
İlk başta radikal görünen seçimler bile, daha derin anlaşıldıkça anlamlı gelmeye başlıyor
Zig, meraklı geliştiricileri ödüllendiren bir dil
Zig’in iyi yanlarından biri, düşük seviyeli sistem kodunun gerçeklerini kabul etmesi
Pek çok dil estetik nedenlerle bu kısımları görmezden gelirken Zig bunu yapmıyor
page_allocator dokümantasyonu bakınız