Tanıtım
C++'nın en büyük ustalarından olan Andrei Alexandrescu, şimdilerde enerjisini Walter Bright tarafından tasarlanmış olan D programlama dilini geliştirmeye harcıyor. Alexandrescu, çeşitli nedenlerle C++'ya eklenemeyen çoğu dil olanağının D'ye eklenmesine yardım ederek, bir anlamda D'yi C++'nın olmayı başaramadığı dil haline getiriyor.
Kendisine özgü heyecanlı tarzını içeren bu yazısında Alexandrescu, D dilinin neden önemli olduğunu ve belki de sizin için de uygun bir dil olabileceğini göstermeye çalışıyor.
Bu yazının İngilizce aslı ilk olarak ACCU'nun yayın organlarından CVu'nun Mayıs 2009 sayısında yayınlanmıştır. Bütün hakları yazarı Andrei Alexandrescu'ya aittir. Aynı yazı, bu çevirinin yapıldığı tarihten kısa bir süre sonra Doctor Dobbs Journal'ın sitesinde de yayınlanacaktır.
Neden D
Yazar: Andrei Alexandrescu
Bakalım D, üzerinde durmaya değecek bir programlama dili mi...
Sizi kolayca ikna edebileceğim yanılgısına düşmeyeceğim. Biz programcılar dil tercihi konusunda oldukça garibizdir. Bir programcının kitapçıda rastladığı "Falanca Programlama Dili" isimli bir kitaba karşı tepkisi herhalde şunun gibi bir şeydir: "Kendime bu dilde hoşlanmadığım bir şey bulana kadar 30 saniye süre tanıyorum." Bir programlama dilini edinmek oldukça uzun ve güç bir iştir. Getireceği tatmin de hem çok uzun vadede gelir, hem de kesin bile değildir. Böyle zorlu bir maceraya atılmayı daha başından engelleyecek nedenler aramak aslında bir savunma mekanizması olarak görülmelidir. Şüpheli ve riskli bir yatırım olduğu için, bu konuda çabucak olumsuz bir karar vermek aslında programcı için büyük bir rahatlıktır.
Öte yandan, bir programlama dili öğrenmek ve kullanabilmek çok zevkli bir uğraştır da. Bir dilin zevkli kabul edilebilmesi için genellikle programcının değer verdiği ilkeleri tatmin edici derecede karşılıyor olması gerekir. Bu konudaki bir uyumsuzluk; programcının o dilin baştan savma, güvensiz, kurumlu, ve usandırıcı olduğunu düşünmesine neden olacaktır. Bir dil her ihtiyacı ve zevki eşit ölçüde karşılayamayacağı için, programcılık alanındaki bir kaç sağlam temel üzerine kurulmuş olması önemlidir.
Nedir D'nin hikayesi? D'yi zaten duymuş olabilirsiniz: garip dil isimlerinden birisinin sahibi, konu dışı olduğu şeklinde uyarılana kadar başka dillerin forumlarında adı duyulan, bir arkadaş tarafından belki hararetle bahsedilmiş olan, veya "Kesin D adında bir dil de vardır" diye düşünerek aratınca karşılaşılan bir dil...
Bu yazıda bu dile çok geniş bir açıdan bakacağım ve bu yüzden dilin bazı kavramlarının ve özelliklerinin üzerinde fazla duramayacağım. Bu yazıda kaynakça da göstermiyorum ama nasıl olsa sözü geçen terimler hakkında daha fazla bilgi edinmek için google.com'dan yararlanabilirsiniz. [Çevirenin notu: Yardımı olacağını düşündüğüm yerlerde terimlerin İngilizce asıllarını da köşeli parantezler içinde vereceğim.]
İşe D'nin temel olanaklarını gözden geçirmekle başlayalım. Olanaklarının ve kısıtlamalarının bazıları doğal olarak biraz muallak kalacaklar. Hoşunuza gitmeyen bir şey okuduğunuzda buna fazla takılmayın çünkü anlam bir sonraki cümlede tamamlanıyor olabilir. Örneğin diyelim "D çöp toplamalı [garbage collection] bir dildir" ifadesini okuduğunuzda iliklerinize kadar dondunuz ve oradan hemen uzaklaşma ihtiyacı hissettiniz... Biraz sabır gösterirseniz, D'de kurucu [constructor] ve bozucu [destructor] işlevlerin de bulunduğunu, ve isterseniz nesne yaşam süreçlerini sizin de belirleyebileceğinizi göreceksiniz.
Konuya girmeden önce
Konuya girmeden önce bilmeniz gereken bir kaç şey var. Öncelikle, eğer daha önceden de D'ye şöyle bir bakmaya karar vermiş ve vazgeçmişseniz, bu sefer de öyle olacağını düşünmeyin. Çünkü içinde bulunduğumuz bu dönem, erken başlamanın getirdiği avantajlar açısından öncekilerden çok farklı bir dönem. D gelişmesini çok hızlı ama biraz sessiz olarak sürdürmekte ve ondaki müthiş gelişmeler tam da şu sıralarda duyulmaya başlanmakta... Hatta bazı gelişmeler ilk olarak bu yazı aracılığıyla duyuruluyor. Bu yazıyı hazırladığım sırada üçte biri bitmiş olan "The D Programming Language" isimli kitabımı tamamlamaya ve bir kaç ay içinde çıkartmaya çalışıyorum.
Bu hızlı gelişme süreci doğal olarak bendenizi sürekli hareket halindeki bir hedefin peşinde koşmak durumunda bırakıyor. Uzun süre güncel kalacak olan bir yazı çıkartmaya karar vermiş olsam da, ne yazık ki biraz da moral bozucu olarak bu yazıda ya hiç gerçekleştirilmemiş ya da henüz yarım olarak gerçekleştirilmiş olanaklardan da söz etmek zorunda kalacağım.
Bu dilin D1 ve D2 olarak başlıca iki sürümü var. Ben bu yazıda yalnızca D2 üzerinde duruyorum. D1 şu anda zaten çok kararlı bir durumda: artık üzerinde değişiklik yapılmıyor ve yalnızca hataları gideriliyor. D2 ise önceki sürümlerle uyumluluk konusunda özveride bulunma kararı almış başlıca bir sürüm... Bu özverinin nedeni, kararlı bir şekilde daha iyiye doğru gitmek, ve çok çekirdekli işlemcilerle [manycores] ve türden bağımsız programlamayla [generic programming] ilgili önemli bazı olanaklar getirmektir. Tabii bunun sonucunda dilin karmaşıklığı da artmış oluyor; ama gerçek hayatta kullanılan hiçbir dil zaten hiç küçülmemiş, hep büyümüştür. Küçük ve hoş olma kaygısıyla başlayan diller bile kullanıldıkça büyümek zorunda kalmışlardır. (Burada ayrıntıya girmeyelim ama evet, Lisp bile.) Programcılar hep küçük ve sade dillerin düşünü kurarlar ama kendilerine geldiklerinde asıl aradıklarının hep daha fazla modelleme yeteneği olduğunu farkederler.
Resmî olarak kabul edilen D derleyicisi dmd, bütün yaygın platformlar (Windows, Mac ve Linux) için digitalmars.com'dan edinilebilir. Başka ortamlara uygun sürümlerinin çalışmaları da devam etmekte; özellikle .NET'e taşınmakta olduğunu belirtmekte yarar var. Ayrıca iki tane de temel D kütüphanesi mevcut: resmî olarak kabul edilen Phobos, ve çok sağlam bir kütüphane olarak tanınan Tango. Başlangıçta D1 için tasarlanmış olan Tango, şu sırada D2'ye taşınıyor. D1 sürümü sinir bozucu derecede küçük ve garip olarak tanınan Phobos ise D2'nin tüm yeteneklerinden yararlanabilmek için köklü değişikliklerden geçiyor. (Tahmin edileceği gibi kütüphanelerden hangisinin daha iyi olduğu konusunda politik görüşler ve atışmalar da var; ama bu rekabet sonuçta her ikisinin de daha iyiye gitmesine yardım ediyor.)
Son olarak, gayet yaygın olan Qt pencereleme kütüphanesi de kısa bir süre önce D desteği vermeye başladı (bunu yazdığım sırada henüz alfa sürümündeler). Bu çok önemli bir haber, çünkü Qt taşınabilir görsel programlar yazabilmek için çok önemli bir ortamdır (kimisine göre de en iyisidir); ve yaygın kullanımdaki bütün işletim sistemlerini destekler. Qt'nin bu D desteği, D'yi bir anlamda görselinci boyuta taşımakta ve sunduğu olanakları mükemmel bir şekilde tamamlamaktadır. Daha da iyisi, bu gelişme tam da Qt'nin LGPL lisansını desteklemeye başlamasının hemen ardından geldi. Ticari programlar Qt'yi artık herhangi bir kısıtlama altında kalmadan ve lisans ücreti ödemeden kullanabiliyorlar.
D'nin temelleri
D'nin en doğru tanımı, üst düzey sistem programlama dili olarak yapılabilir. Normalde üst düzey dillerde ve hatta betik dillerde [script language] görmeye alıştığımız bazı olanaklara sahiptir: çok hızlı kodlama-derleme-çalıştırma süreci, çöp toplama, dile yerleşik hızlı eşleme tabloları [hash tables], tür bildirimlerini yazmak zorunda olmamak, vs. Ama aynı zamanda alt düzey olanaklar da sunar: işaretçiler, elle (malloc/free) veya yarı-otomatik (kurucular ve bozucular) olarak yapılabilen kaynak yönetimi, ve bellek ile C ve C++ programcılarının çok sevdikleri gibi doğrudan etkileşebilme olanağı. Hatta D, C fonksiyonlarını hiç bir dönüşüm gerektirmeden çağırabilir. Yani C standart kütüphanesinin tamamı D programcılarının kullanımına hazır durumdadır. Ama genelde o kadar alt düzeye inme ihtiyacı hissedilmez; çünkü hem D'nin kolaylıkları çoğu zaman daha güçlü ve daha güvenlidir, hem de zaten D alt düzey programlama kadar etkin kod üretir. Genelde D'de kolaylık ve verimlilik arasında seçim yapmak gerekmez.
D'nin çok paradigmalı [multi-paradigm] olduğu söylenebilir: kodlama olarak nesne yönelimli [object-oriented], fonksiyonel [functional], türden bağımsız [generic], ve yordamsal [procedural] programlama tarzlarını destekler. D'deki bazı genel kavramları aşağıdaki küçük bölümlerde bulacaksınız.
Biraz haksızca sataşarak merhaba
Daha fazla uzatmadan şu söz dizimi konusunu aradan çıkartalım:
Söz dizimi biraz giysi konusuna benzer; mantıklı düşününce giysilerin önemli olmaması gerektiğini biliriz. Hatta bu konuya fazla önem vermenin de biraz sığlık olarak kabul edilebileceğini de görebiliriz; ama giysi konusu hemen dikkatimizi çeker. (The Matrix filmindeki kırmızı elbiseli kızı bugün bile hatırlarım.) C'nin söz dizimine benzerliği nedeniyle D çoğumuza tanıdık gelecektir. Bu benzerliği C++, Java ve C#'ta da görmekteyiz. (Bu dillerden en az birisini bildiğinizi varsayıyorum ve tam sayıların, kayan noktalı sayıların, dizilerin, erişicilerin [iterator] ve özyinelemenin D'de de bulunduğunu söyleme gereği duymuyorum.)
Hazır başka dillerden söz etmişken, C ve C++'nın "merhaba dünya" programlarına biraz haksızca da olsa sataşalım. C programının klasik hali K&R'ın ikinci basımında şöyledir:
ve aynı derecede klasik olan C++ programı da şöyledir (heyecandaki fazlalığa dikkat edin):
Bu tanıdık programın değişik dillerdeki halleri karşılaştırılırken çoğunlukla kod uzunluğuna ve programı anlamak için gereken bilgi miktarına bakılır. Bu sefer değişik bir yol çizelim ve doğruluktan söz edelim:
Mesaj standart çıkışa gönderilemezse ne olur? C programı hatayı gözardı eder, çünkü printf'in dönüş değerine bakmamaktadır. Gerçeği söylemek gerekirse, aslında durum daha da kötüdür: C programı benim ortamımda hatasız ve uyarısız olarak derleniyor olsa bile işletim sistemine belirsiz bir değer döndürür, çünkü program akışı main'den bir return deyimi olmaksızın çıkmaktadır. (Ubuntu'da hep 13 değerini gördüğüm için biraz ürktüğümü söyleyebilirim.) Hatta program C89 ve C99 standartlarına uymaz bile. Biraz araştırma sonucunda İnternet'ten de öğrenilebileceği gibi, bu selamlamanın doğrusu C'de aslında şöyle olmalıdır:
Ama onun da doğruluğu hâlâ şüphelidir; çünkü belirsiz bir dönüş değeri yerine, bu sefer de mesajın yazdırılıp yazdırılmadığından bağımsız olarak hep başarılı sonlanma anlamına gelen 0 değerini döndürmektedir.
return deyimi unutulduğunda C++ programının 0 döndüreceği standart tarafından garanti edilmiştir ama o da hatayı gözardı etmektedir; çünkü program başladığında std::cout.exceptions()'ın değeri sıfırdır ve kimse std::cout.bad()'e bakmamaktadır. Sonuçta mesaj doğru olarak yazdırılmamış bile olsa, her iki program da başarılı sonlandığını iddia etmektedirler. Yani aslında C ve C++ programlarının düzeltilmiş halleri alışık olduğumuzdan daha gösterişsizdirler:
Biraz araştırınca, "merhaba dünya" programının Java (yer darlığı nedeniyle kodunu göstermiyorum), J# (Java ile en ufak bir ilgisi olmayan bir dil), ve Perl gibi başka dillerde de her durumda başarı iddiasında bulunulduğunu görmekteyiz. Tam bir komployla karşı karşıya olduğumuzu düşünmek üzereyken, neyse ki Python ve C#'ta öyle olmadığını görüyoruz ve rahatlıyoruz: mesaj yazdırılamadığında ikisinde de hata [exception] atılır.
Peki bu programın D halinde durum nasıl? Hiçbir değişiklik gerekmez, çünkü bir sorun çıktığında writeln hata atar; main içinde oluşan hatalarla ilgili mesajlar eğer mümkünse standart hata akımına yazdırılırlar; ve program da bir hata koduyla sonlanır. Yani program yazıldığı haliyle zaten doğru çalışmaktadır. Diğer dillere böyle haksızca sataşmamın iki nedeni var. Birincisi, "merhaba dünya" programları hep böyle hileliler diye milyonlarca programcının sokaklara dökülerek ayaklandığını hayal etmek oldukça komik. (Gözünüzde canlandırın: "Merhaba dünya! Çıkış kodunun 13 olması bir tesadüf mü?" veya "Merhaba koyunlar! Uyanın artık!" vs.) İkincisi, maalesef bu programlar yaygın bazı programcı davranışlarını sergilemektedirler. Öte yandan D, doğru olanı yapmanıza izin vermek yanında, en kolay yöntemin aynı zamanda en doğru yöntem olduğunu da sağlamaya çalışmaktadır. Kolaylık ve doğruluk arasındaki bu özdeşlik, programcılıkta aslında sandığımızdan çok daha sık olarak karşımıza çıkar. (Bu arada benim kodumun yanlış olduğunu da düşünmeyin; void main() D'de yasaldır ve tam da düşündüğünüz şekilde çalışır. Sonuçta, yeni başlayanları int main() yerine void main() yazdılar diye C++ forumlarında haşlayan titizler, D'ye geçtiklerinde kendilerine yeni uğraşlar bulmak zorunda kalacaklar.)
Söz diziminden bahsedecekken anlam konularına kaydık. Söz dizimine dönersek; D'de C++, C# ve Java'da olmayan dikkate değer bir fark var: D'de parametreli türler T<X, Y, Z> yerine T!(X, Y, Z) olarak gösterilirler (T<X> yerine de T!(X) veya daha kısaca T!X kullanılır). C++'da bu konuda açılı parantezlerin seçilmiş olması; <, >, ve >> işleçleriyle çakıştığı için büyük gramer sorunlarına yol açmıştır. Böyle belirsiz durumları gidermek için de rasgele kabul edilebilecek kurallar gerekmiştir. Hatta dünyanın en az bilinen söz dizimi kuralı da bu şekilde ortaya çıkmıştır: nesne.template fonksiyon<arguman>(). Süpermen düzeyinde iyi C++'cı olan bir arkadaşınıza o söz diziminin ne anlama geldiğini sorun; arkadaşınızın büyük olasılıkla Kriptonit kullanmak zorunda kaldığını göreceksinizdir. Açılı parantezler Java ve C#'ta da kullanılmaktadır fakat o diller aritmetik ifadelerin parametre olarak kullanılmasına izin vermezler; ama sonuçta böyle bir olanağın ileride eklenme şansı da ortadan kalkmıştır. D bu konuda geleneksel birli ! işlecini ikili olarak kullanır ve parametreleri geleneksel parantezlerle gösterir (parantezleri hep doğru sayıda kapatıyorsunuzdur (değil mi?)).
Derleme modeli
D'de derleme, erişim denetimi, ve modül kavramlarının temeli dosyadır. Paketleme kavramının temeli de klasördür; bu konuda daha fazla karmaşıklık getirmez. Program kodunun daha gelişmiş bir veri tabanında bulunmasına gerek görülmez. D'nin yaklaşımı olan dosya ve klasör kavramı da zaten bir tür veri tabanıdır. En iyi programcılar tarafından uzun emekler sonucunda geliştirilmiş olan dosya ve klasör modeli sayesinde şu araçları da hazır olarak kullanma şansımız doğar: sürüm denetimi [version control], yedekleme, işletim sistemi düzeyinde koruma, günlükleme [journaling], vs. Böylece program geliştirmeye başlamak için gereken araçlar da ikiye inmiş olur: metin düzenleyici ve derleyici. Aslında D'de araçlar konusunda henüz fazla gelişme kaydedildiğini söyleyemeyiz ama yine de şunlar mevcut: Emacs'te d-mode modu, Eclipse'in Descent eklentisi, Linux'ta hata ayıklayıcı ZeroBugs, ve Poseidon geliştirme ortamı...
D'de kod üretimi bildiğimiz derleme ve bağlama adımlarından oluşur ama benzerlerinden çok daha hızlıdır; bunun iki, hayır üç nedeni var. Birincisi; dilin grameri sözcükleme [lexing], ayrıştırma [parsing], ve çözümleme [analysis] adımlarını birbirlerinden ayrı ve çok hızlı olarak yapmaya olanak verir. İkincisi; başka derleyicilerin çoğunda olduğu gibi program parçalarını [object file] ayrı ayrı oluşturmak yerine, D derleyicisine herşeyi önce bellekte oluşturmasını ve en sonunda diske yazmasını söyleyebilirsiniz. Üçüncüsü; D'nin yaratıcısı ve ilk gerçekleştiricisi olan Walter Bright, en iyileme [optimization] konusunda son derece deneyimli ve uzman birisidir. Geliştirme aşamalarındaki beklemelerin az olması D'yi çok güçlü bir yorumlayıcı [interpreter] haline de getirir (#!/bin/sh yazımındaki gibi #! bile desteklenir).
D ayrı ayrı derlemeyi destekleyen gerçek bir modül sistemine sahiptir. Modül özetlerini (başlık dosyalarının entel ismi) kaynak dosyalarından otomatik olarak öğrenebilir. Böylece başlık dosyalarıyla kendimiz ilgilenmek zorunda kalmayız; ama istersek bizim yazmamıza da izin verilir; ve bu konunun şikayet edilecek tarafı da kalmamış olur.
Bellek modeli ve çok çekirdekliler [manycores]
C fonksiyonlarını doğrudan çağırıyor olması D'nin de C bellek modeli üzerine kurulu olduğunu düşündürmüş olabilir. Öyle olabilse gerçekten iyi olurdu ama ne yazık ki bu, paralel mimarileri ile yüksek işlem gücü sağlayan çok çekirdekli işlemciler yüzünden olanaksızdır. Çok çekirdekliler artık günlük hayatımızdalar; C'nin bu konudaki yaklaşımı ise ne yazık ki çok sıradan ve hataya açık olarak kalmıştır. Yordamsal ve nesne yönelimli bazı başka diller ise bu konuda ancak pek az gelişme gösterebilmişlerdir ve bu durum, paralel işlemlerle ilgili sorunları değişmezlik [immutability] kavramının yardımıyla aşan fonksiyonel dillerin tekrardan gündeme gelmesine neden olmuştur.
Yeni bir dil olduğu için iş parçacıkları [threads] konusunda D çok şanslı bir durumdadır; D'nin bellek modeli diğer modellerden köklü farklılıklar içerir. İş parçacıklarıyla şu şekilde çalışmaya alışmışızdır: iş parçacığı başlatmak için bir fonksiyon çağrılır ve bu yeni iş parçacığı bütün programın belleğini görebilen ve değiştirebilen bir duruma geliverir. Veya seçime bağlı olarak, işletim sistemine bağlı bazı yöntemler kullanılarak iş parçacığına özel [thread-private] bellek de kullanılabilir. Şimdiye kadar yalnızca bir sorun olarak görülen bu durum, günümüzde bir kabus halini almıştır. Dünün sorunları, verinin eş zamanlı olarak değişmesinin getirdiği doğal bir sonuçtu: verinin hep geçerli bir durumda olmasını sağlamak amacıyla bütün değişikleri takip etmek, ve değişikliklerin doğru sırada yapılmalarını hedeflemek son derece güç bir iştir. Ama insanlar yine de bu durumu kabul etmek zorundaydılar; çünkü paylaşımlı bellek, donanımı çok sadık olarak modellemekteydi ve doğru çalıştığında çok verimli olmaktaydı. Şimdi kabus konusuna geliyoruz: günümüzde bellek eskiden olduğundan daha az paylaşımlıdır. Bugünkü donanımlarda işlemciler belleği daha katmanlı bir şekilde kullanmaktadırlar; her bir çekirdeğin kendine özel bir belleği vardır! Sonuçta paylaşımlı bellek yalnızca zor yöntem olmakla kalmamış, yavaş olan yöntem haline de gelmiştir; çünkü paylaşımlı bellek, artık donanımı sadık bir şekilde modellememektedir.
Geleneksel diller bu tür sorunlarla uğraşırken, fonksiyonel diller bu konuya matematiksel saflıkla yaklaşıyorlardı: donanımı modellemekle değil, gerçek matematiği modellemekle ilgileniyorlardı. Matematik çoğunlukla değişim [mutation] içermediğinden ve zamandan bağımsız olduğundan, paralel işlemler için çok uygundur. (Matematikçilikten dönme o ilk programcıların paralel işlemeyi ilk duyduklarında nasıl sevinmiş olabileceklerini hayal edebiliyorum.) Böyle bir modelin sırasız ve paralel işlemlere ne kadar uygun olduğu fonksiyonel programcılıkta başından beri biliniyordu. Ama bu uygunluk daha çok saklı bir enerji olarak duruyordu; günümüzde olduğu gibi bir amaç olarak görülmüyordu. Paralel işlemler kullanan ciddi programların en azından bazı bölümlerinin fonksiyonel ve değişimden bağımsız olarak yazılmalarının önemi günümüzde daha iyi anlaşılmaktadır.
Bu konuda D'nin aldığı tutum nedir? D'nin paralelliğe yaklaşımı temel bir kavram üzerine kuruludur: Belleğin öncelikle iş parçacığına özel olduğu varsayılır, ve ancak istendiğinde paylaşımlı olarak kullanılır.
D'de bütün bellek, hatta globaller bile, öncelikle onu kullanan iş parçacığına özeldir. Paylaşılmak istenirse nesneler shared anahtar sözcüğü ile bildirilirler ve ancak ondan sonra başka iş parçacıkları tarafından da görülebilirler. Burada önemli olan, tüm sistemin shared olan nesnelerden haberli olması, ve o nesnelere erişimin doğru sırada yapılmasını sağlamak için bazı kısıtlamalar getirmesidir. Bu model, nesnelerin paylaşımlı olduklarını varsayan dillerdeki bir sürü belalı sorunu böylece ortadan kaldırır. O dillerde nesnelerin hangilerinin paylaşıldıkları bilinmediği için programcıya güvenilmekte ve programcının nesneleri doğru olarak bildirmiş ve kullanmış olması beklenmektedir. Ondan sonra da çeşitli durumları açıklamak için karmaşık kurallar gerekmiştir: paylaşılmayan veriler, paylaşımlı olarak bildirilmiş olan veriler, paylaşımlı oldukları bildirilmediği halde paylaşımlı olan veriler, ve bunların her türlü kombinasyonu... Bu kurallar da onları anlayabilen topu topu beş kişi için gayet açıkça yazılmıştır ve geri kalanlarımıza da pes etmekten başka çare kalmamıştır.
Çok çekirdekliler konusunda araştırma ve geliştirme çabaları yoğun olarak devam ettiği halde hâlâ iyi bir model bulunamamıştır. Ama özel bellek modeli üzerine kurulu olduğu için D'nin bu konuda önü açıktır: saf fonksiyonlar, kilitsiz nesneler [lock-free primitives], alışık olduğumuz kilitlere dayalı programlama, mesaj kuyrukları (henüz plan aşamasında), vs. Sahiplenme türleri gibi daha ileri konular da halen tartışılmaktadır.
Değişmezlik [Immutability]
Buraya kadar tamam... Pekiyi matematiğin saflığı, değişmezlik, ve fonksiyonel kodlama gibi konulara ne oldu? D, fonksiyonel programlamanın ve değişmezlik kavramının paralel programlamadaki (ve başka konulardaki) önemini kabul eder ve hiçbir şekilde değişmeyecek olan nesneleri bildirmek için immutable anahtar sözcüğünü getirir. Aynı zamanda, D çoğumuzun yakından tanıdığı değişkenlik kavramını ve bu kavramın en iyi çözüm olduğu durumların varlığını da kabul eder. (Eğer siz çoğumuza dahil değilseniz, bazılarımız demiş olayım.) D'nin bu soruna yanıtı ilginçtir, çünkü böylece değişken ve değişmez nesneler kusursuzca bir araya getirilmiş olurlar.
Değişmez veriler neden bu kadar yararlıdır? Değişmez verilerin iş parçacıkları tarafından paylaşılmalarında erişim sırasının [synchronization] önemi ortadan kalkar. Doğal olarak da, hiç yapılmayan sıralama en hızlı sıralamadır... Buradaki ustalık, salt okunan [read-only] verilerin gerçekten yalnızca okunduklarını sağlamaktadır; yoksa zaten bu konuda hiçbir güvenceden söz edemeyiz. D, paralel programlamanın bu önemli özelliğini sağlayabilmek için fonksiyonel programlamaya eşi görülmemiş derecede destek verir. immutable olarak bildirilen verilerin değişmezlikleri derleme zamanında belirlenir; ve doğru yazılmış olan bir program immutable veriyi kesinlikle değiştiremez. Dahası, değişmezlik derinlemesinedir: değişmez bir alan içerisinden referansla geçtiğiniz başka bir alan da değişmezdir. (Neden? Çünkü öyle olmadığı zaman bütün sistem zayıflar: değişken verileri farkında olmadan paylaşmaya başlarız ve önlemeye çalıştığımız karmaşık kurallarla tekrar karşı karşıya geliriz.) Birbirlerine bağlı nesnelerin oluşturdukları alt grafların tamamı kolaylıkla immutable olarak bildirilebilirler. D'nin tür sistemi bunları tanıdığı için iş parçacıkları tarafından paylaşılmaları sağlanır ve tek işli [single-threaded] programlar için de eniyileme olanakları doğmuş olur.
Pekiyi özel bellek modelini benimseyen ilk dil D midir? Hiç de değil. D'yi diğerlerinden ayrı kılan, değişken ve değişmez verileri öncelikle özel olarak kabul ettiği tek bir bellek sistemi altında barındırmasıdır. Aslında bu konuda anlatacak çok ayrıntı var; biz yine tanıtıma devam edelim.
Güvenlik ön planda
Bir sistem dili olması nedeniyle D, son derece verimli ama bir o kadar da tehlikeli olanaklar sunar: denetimsiz işaretçiler, programcının elle yapabildiği bellek denetimi, ve en dikkatli tasarımları bile mahvedebilecek tür dönüşümleri. Bununla beraber, modüllerin güvenli olduklarını bildirebilme olanağı ve o olanağı desteklemek için bellek güvenliği sağlayan ve SafeD olarak adlandırılan bir derleme şekli de sunar. Yine de bu şekilde başarılı olarak derlenmiş olması; ne kodun taşınabilir olduğunu, ne güvenli olarak programlandığını, ne de birim testler [unit testing] gerektirmediğini gösterir. SafeD yalnızca bellek hatalarının [memory corruption] olasılığını azaltır.
Böyle güvenli modüller (veya güvenli derleme); işaretçileri olduklarından farklı kullanma ve yığıt nesnelerinin adreslerini fonksiyonlardan döndürme gibi bütün tehlikeli dil olanaklarını daha derleme zamanında önler. SafeD'de bellek hatalarıyla karşılaşamazsınız. Aslında bütün büyük programların kodlarının çoğunluğu böyle güvenli modüllerden oluşmalıdır; bu programların sistem modüllerinin az sayıda olmasına çalışılmalı, ve sistem modüllerinin kod incelemeleri [code review] çok dikkatlice yapılmalıdır. Çoğu program tamamen SafeD ile yazılabilir, ama tabii bellek ayırma gibi işleri halleden bazı modülleri yazarken biraz el emeği de gerekecektir. Böylelikle, programın bazı bölümlerini başka bir dilde yazmak gerekmemiş olur. SafeD henüz hazır değil; bu yazı yazıldığı sırada SafeD'nin gelişimi halen devam etmekteydi.
Kabul, başka yollar da olabilir
D'nin çok paradigmalı olduğunu söylerken, bazı seçimler sırasında çatışmaların gerekmeyebileceğini kastediyoruz. ["It doesn't have an axe to grind."] D bu konuyu kavramıştır... Herşeyin nesne, fonksiyon, liste, eşleme tablosu, veya Noel Baba olması gerekmez. Ne oldukları size kalmış olmalıdır. Bu yüzden D programcılığı özgürlükler getirir, çünkü bir sorunu çözmeye çalışırken eldeki olanakları o çözüme uydurmak için çabalamak gerekmez. Buradaki bir gerçeği de dile getirmek gerekiyor: özgürlüklerle birlikte sorumluluklar da gelir; bu sefer de çözüm için hangi tasarımın daha uygun olacağına karar vermek için zaman harcamak gerekecektir.
D bu konuda C++'nın izinden yürür; doğru olan tek yol gibi bir ısrarı yoktur. Ek olarak, D her paradigmaya daha fazla destek verir, paradigmalar arasında daha fazla uyum sağlar, ve seçilen paradigmaların izlenmesinde daha az engel çıkartır. Burada aslında iyi bir öğrenciyle karşı karşıyayız; D C++'tan çok şey öğrenmiştir. Daha az seçici diller olan Java, Haskell, Eiffel, Javascript, Python, ve Lisp'ten de etkilenmiştir. (Aslında çoğu dil Lisp'ten bir şeyler almıştır ama bazıları bunu itiraf edemez.)
Alt bölümde devam ediyor...
C++'nın en büyük ustalarından olan Andrei Alexandrescu, şimdilerde enerjisini Walter Bright tarafından tasarlanmış olan D programlama dilini geliştirmeye harcıyor. Alexandrescu, çeşitli nedenlerle C++'ya eklenemeyen çoğu dil olanağının D'ye eklenmesine yardım ederek, bir anlamda D'yi C++'nın olmayı başaramadığı dil haline getiriyor.
Kendisine özgü heyecanlı tarzını içeren bu yazısında Alexandrescu, D dilinin neden önemli olduğunu ve belki de sizin için de uygun bir dil olabileceğini göstermeye çalışıyor.
Bu yazının İngilizce aslı ilk olarak ACCU'nun yayın organlarından CVu'nun Mayıs 2009 sayısında yayınlanmıştır. Bütün hakları yazarı Andrei Alexandrescu'ya aittir. Aynı yazı, bu çevirinin yapıldığı tarihten kısa bir süre sonra Doctor Dobbs Journal'ın sitesinde de yayınlanacaktır.
Neden D
Yazar: Andrei Alexandrescu
Bakalım D, üzerinde durmaya değecek bir programlama dili mi...
Sizi kolayca ikna edebileceğim yanılgısına düşmeyeceğim. Biz programcılar dil tercihi konusunda oldukça garibizdir. Bir programcının kitapçıda rastladığı "Falanca Programlama Dili" isimli bir kitaba karşı tepkisi herhalde şunun gibi bir şeydir: "Kendime bu dilde hoşlanmadığım bir şey bulana kadar 30 saniye süre tanıyorum." Bir programlama dilini edinmek oldukça uzun ve güç bir iştir. Getireceği tatmin de hem çok uzun vadede gelir, hem de kesin bile değildir. Böyle zorlu bir maceraya atılmayı daha başından engelleyecek nedenler aramak aslında bir savunma mekanizması olarak görülmelidir. Şüpheli ve riskli bir yatırım olduğu için, bu konuda çabucak olumsuz bir karar vermek aslında programcı için büyük bir rahatlıktır.
Öte yandan, bir programlama dili öğrenmek ve kullanabilmek çok zevkli bir uğraştır da. Bir dilin zevkli kabul edilebilmesi için genellikle programcının değer verdiği ilkeleri tatmin edici derecede karşılıyor olması gerekir. Bu konudaki bir uyumsuzluk; programcının o dilin baştan savma, güvensiz, kurumlu, ve usandırıcı olduğunu düşünmesine neden olacaktır. Bir dil her ihtiyacı ve zevki eşit ölçüde karşılayamayacağı için, programcılık alanındaki bir kaç sağlam temel üzerine kurulmuş olması önemlidir.
Nedir D'nin hikayesi? D'yi zaten duymuş olabilirsiniz: garip dil isimlerinden birisinin sahibi, konu dışı olduğu şeklinde uyarılana kadar başka dillerin forumlarında adı duyulan, bir arkadaş tarafından belki hararetle bahsedilmiş olan, veya "Kesin D adında bir dil de vardır" diye düşünerek aratınca karşılaşılan bir dil...
Bu yazıda bu dile çok geniş bir açıdan bakacağım ve bu yüzden dilin bazı kavramlarının ve özelliklerinin üzerinde fazla duramayacağım. Bu yazıda kaynakça da göstermiyorum ama nasıl olsa sözü geçen terimler hakkında daha fazla bilgi edinmek için google.com'dan yararlanabilirsiniz. [Çevirenin notu: Yardımı olacağını düşündüğüm yerlerde terimlerin İngilizce asıllarını da köşeli parantezler içinde vereceğim.]
İşe D'nin temel olanaklarını gözden geçirmekle başlayalım. Olanaklarının ve kısıtlamalarının bazıları doğal olarak biraz muallak kalacaklar. Hoşunuza gitmeyen bir şey okuduğunuzda buna fazla takılmayın çünkü anlam bir sonraki cümlede tamamlanıyor olabilir. Örneğin diyelim "D çöp toplamalı [garbage collection] bir dildir" ifadesini okuduğunuzda iliklerinize kadar dondunuz ve oradan hemen uzaklaşma ihtiyacı hissettiniz... Biraz sabır gösterirseniz, D'de kurucu [constructor] ve bozucu [destructor] işlevlerin de bulunduğunu, ve isterseniz nesne yaşam süreçlerini sizin de belirleyebileceğinizi göreceksiniz.
Konuya girmeden önce
Konuya girmeden önce bilmeniz gereken bir kaç şey var. Öncelikle, eğer daha önceden de D'ye şöyle bir bakmaya karar vermiş ve vazgeçmişseniz, bu sefer de öyle olacağını düşünmeyin. Çünkü içinde bulunduğumuz bu dönem, erken başlamanın getirdiği avantajlar açısından öncekilerden çok farklı bir dönem. D gelişmesini çok hızlı ama biraz sessiz olarak sürdürmekte ve ondaki müthiş gelişmeler tam da şu sıralarda duyulmaya başlanmakta... Hatta bazı gelişmeler ilk olarak bu yazı aracılığıyla duyuruluyor. Bu yazıyı hazırladığım sırada üçte biri bitmiş olan "The D Programming Language" isimli kitabımı tamamlamaya ve bir kaç ay içinde çıkartmaya çalışıyorum.
Bu hızlı gelişme süreci doğal olarak bendenizi sürekli hareket halindeki bir hedefin peşinde koşmak durumunda bırakıyor. Uzun süre güncel kalacak olan bir yazı çıkartmaya karar vermiş olsam da, ne yazık ki biraz da moral bozucu olarak bu yazıda ya hiç gerçekleştirilmemiş ya da henüz yarım olarak gerçekleştirilmiş olanaklardan da söz etmek zorunda kalacağım.
Bu dilin D1 ve D2 olarak başlıca iki sürümü var. Ben bu yazıda yalnızca D2 üzerinde duruyorum. D1 şu anda zaten çok kararlı bir durumda: artık üzerinde değişiklik yapılmıyor ve yalnızca hataları gideriliyor. D2 ise önceki sürümlerle uyumluluk konusunda özveride bulunma kararı almış başlıca bir sürüm... Bu özverinin nedeni, kararlı bir şekilde daha iyiye doğru gitmek, ve çok çekirdekli işlemcilerle [manycores] ve türden bağımsız programlamayla [generic programming] ilgili önemli bazı olanaklar getirmektir. Tabii bunun sonucunda dilin karmaşıklığı da artmış oluyor; ama gerçek hayatta kullanılan hiçbir dil zaten hiç küçülmemiş, hep büyümüştür. Küçük ve hoş olma kaygısıyla başlayan diller bile kullanıldıkça büyümek zorunda kalmışlardır. (Burada ayrıntıya girmeyelim ama evet, Lisp bile.) Programcılar hep küçük ve sade dillerin düşünü kurarlar ama kendilerine geldiklerinde asıl aradıklarının hep daha fazla modelleme yeteneği olduğunu farkederler.
Resmî olarak kabul edilen D derleyicisi dmd, bütün yaygın platformlar (Windows, Mac ve Linux) için digitalmars.com'dan edinilebilir. Başka ortamlara uygun sürümlerinin çalışmaları da devam etmekte; özellikle .NET'e taşınmakta olduğunu belirtmekte yarar var. Ayrıca iki tane de temel D kütüphanesi mevcut: resmî olarak kabul edilen Phobos, ve çok sağlam bir kütüphane olarak tanınan Tango. Başlangıçta D1 için tasarlanmış olan Tango, şu sırada D2'ye taşınıyor. D1 sürümü sinir bozucu derecede küçük ve garip olarak tanınan Phobos ise D2'nin tüm yeteneklerinden yararlanabilmek için köklü değişikliklerden geçiyor. (Tahmin edileceği gibi kütüphanelerden hangisinin daha iyi olduğu konusunda politik görüşler ve atışmalar da var; ama bu rekabet sonuçta her ikisinin de daha iyiye gitmesine yardım ediyor.)
Son olarak, gayet yaygın olan Qt pencereleme kütüphanesi de kısa bir süre önce D desteği vermeye başladı (bunu yazdığım sırada henüz alfa sürümündeler). Bu çok önemli bir haber, çünkü Qt taşınabilir görsel programlar yazabilmek için çok önemli bir ortamdır (kimisine göre de en iyisidir); ve yaygın kullanımdaki bütün işletim sistemlerini destekler. Qt'nin bu D desteği, D'yi bir anlamda görselinci boyuta taşımakta ve sunduğu olanakları mükemmel bir şekilde tamamlamaktadır. Daha da iyisi, bu gelişme tam da Qt'nin LGPL lisansını desteklemeye başlamasının hemen ardından geldi. Ticari programlar Qt'yi artık herhangi bir kısıtlama altında kalmadan ve lisans ücreti ödemeden kullanabiliyorlar.
D'nin temelleri
D'nin en doğru tanımı, üst düzey sistem programlama dili olarak yapılabilir. Normalde üst düzey dillerde ve hatta betik dillerde [script language] görmeye alıştığımız bazı olanaklara sahiptir: çok hızlı kodlama-derleme-çalıştırma süreci, çöp toplama, dile yerleşik hızlı eşleme tabloları [hash tables], tür bildirimlerini yazmak zorunda olmamak, vs. Ama aynı zamanda alt düzey olanaklar da sunar: işaretçiler, elle (malloc/free) veya yarı-otomatik (kurucular ve bozucular) olarak yapılabilen kaynak yönetimi, ve bellek ile C ve C++ programcılarının çok sevdikleri gibi doğrudan etkileşebilme olanağı. Hatta D, C fonksiyonlarını hiç bir dönüşüm gerektirmeden çağırabilir. Yani C standart kütüphanesinin tamamı D programcılarının kullanımına hazır durumdadır. Ama genelde o kadar alt düzeye inme ihtiyacı hissedilmez; çünkü hem D'nin kolaylıkları çoğu zaman daha güçlü ve daha güvenlidir, hem de zaten D alt düzey programlama kadar etkin kod üretir. Genelde D'de kolaylık ve verimlilik arasında seçim yapmak gerekmez.
D'nin çok paradigmalı [multi-paradigm] olduğu söylenebilir: kodlama olarak nesne yönelimli [object-oriented], fonksiyonel [functional], türden bağımsız [generic], ve yordamsal [procedural] programlama tarzlarını destekler. D'deki bazı genel kavramları aşağıdaki küçük bölümlerde bulacaksınız.
Biraz haksızca sataşarak merhaba
Daha fazla uzatmadan şu söz dizimi konusunu aradan çıkartalım:
Kod:
import std.stdio;
void main()
{
writeln("merhaba dünya");
}
Söz dizimi biraz giysi konusuna benzer; mantıklı düşününce giysilerin önemli olmaması gerektiğini biliriz. Hatta bu konuya fazla önem vermenin de biraz sığlık olarak kabul edilebileceğini de görebiliriz; ama giysi konusu hemen dikkatimizi çeker. (The Matrix filmindeki kırmızı elbiseli kızı bugün bile hatırlarım.) C'nin söz dizimine benzerliği nedeniyle D çoğumuza tanıdık gelecektir. Bu benzerliği C++, Java ve C#'ta da görmekteyiz. (Bu dillerden en az birisini bildiğinizi varsayıyorum ve tam sayıların, kayan noktalı sayıların, dizilerin, erişicilerin [iterator] ve özyinelemenin D'de de bulunduğunu söyleme gereği duymuyorum.)
Hazır başka dillerden söz etmişken, C ve C++'nın "merhaba dünya" programlarına biraz haksızca da olsa sataşalım. C programının klasik hali K&R'ın ikinci basımında şöyledir:
Kod:
#include <stdio.h>
main()
{
printf("hello, world\n");
}
ve aynı derecede klasik olan C++ programı da şöyledir (heyecandaki fazlalığa dikkat edin):
Kod:
#include <iostream>
int main()
{
std::cout << "Hello, world!\n";
}
Bu tanıdık programın değişik dillerdeki halleri karşılaştırılırken çoğunlukla kod uzunluğuna ve programı anlamak için gereken bilgi miktarına bakılır. Bu sefer değişik bir yol çizelim ve doğruluktan söz edelim:
Mesaj standart çıkışa gönderilemezse ne olur? C programı hatayı gözardı eder, çünkü printf'in dönüş değerine bakmamaktadır. Gerçeği söylemek gerekirse, aslında durum daha da kötüdür: C programı benim ortamımda hatasız ve uyarısız olarak derleniyor olsa bile işletim sistemine belirsiz bir değer döndürür, çünkü program akışı main'den bir return deyimi olmaksızın çıkmaktadır. (Ubuntu'da hep 13 değerini gördüğüm için biraz ürktüğümü söyleyebilirim.) Hatta program C89 ve C99 standartlarına uymaz bile. Biraz araştırma sonucunda İnternet'ten de öğrenilebileceği gibi, bu selamlamanın doğrusu C'de aslında şöyle olmalıdır:
Kod:
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
return deyimi unutulduğunda C++ programının 0 döndüreceği standart tarafından garanti edilmiştir ama o da hatayı gözardı etmektedir; çünkü program başladığında std::cout.exceptions()'ın değeri sıfırdır ve kimse std::cout.bad()'e bakmamaktadır. Sonuçta mesaj doğru olarak yazdırılmamış bile olsa, her iki program da başarılı sonlandığını iddia etmektedirler. Yani aslında C ve C++ programlarının düzeltilmiş halleri alışık olduğumuzdan daha gösterişsizdirler:
Kod:
#include <stdio.h>
int main()
{
return printf("hello, world\n") < 0;
}
Kod:
#include <iostream>
int main()
{
std::cout << "Hello, world!\n";
return std::cout.bad();
}
Biraz araştırınca, "merhaba dünya" programının Java (yer darlığı nedeniyle kodunu göstermiyorum), J# (Java ile en ufak bir ilgisi olmayan bir dil), ve Perl gibi başka dillerde de her durumda başarı iddiasında bulunulduğunu görmekteyiz. Tam bir komployla karşı karşıya olduğumuzu düşünmek üzereyken, neyse ki Python ve C#'ta öyle olmadığını görüyoruz ve rahatlıyoruz: mesaj yazdırılamadığında ikisinde de hata [exception] atılır.
Peki bu programın D halinde durum nasıl? Hiçbir değişiklik gerekmez, çünkü bir sorun çıktığında writeln hata atar; main içinde oluşan hatalarla ilgili mesajlar eğer mümkünse standart hata akımına yazdırılırlar; ve program da bir hata koduyla sonlanır. Yani program yazıldığı haliyle zaten doğru çalışmaktadır. Diğer dillere böyle haksızca sataşmamın iki nedeni var. Birincisi, "merhaba dünya" programları hep böyle hileliler diye milyonlarca programcının sokaklara dökülerek ayaklandığını hayal etmek oldukça komik. (Gözünüzde canlandırın: "Merhaba dünya! Çıkış kodunun 13 olması bir tesadüf mü?" veya "Merhaba koyunlar! Uyanın artık!" vs.) İkincisi, maalesef bu programlar yaygın bazı programcı davranışlarını sergilemektedirler. Öte yandan D, doğru olanı yapmanıza izin vermek yanında, en kolay yöntemin aynı zamanda en doğru yöntem olduğunu da sağlamaya çalışmaktadır. Kolaylık ve doğruluk arasındaki bu özdeşlik, programcılıkta aslında sandığımızdan çok daha sık olarak karşımıza çıkar. (Bu arada benim kodumun yanlış olduğunu da düşünmeyin; void main() D'de yasaldır ve tam da düşündüğünüz şekilde çalışır. Sonuçta, yeni başlayanları int main() yerine void main() yazdılar diye C++ forumlarında haşlayan titizler, D'ye geçtiklerinde kendilerine yeni uğraşlar bulmak zorunda kalacaklar.)
Söz diziminden bahsedecekken anlam konularına kaydık. Söz dizimine dönersek; D'de C++, C# ve Java'da olmayan dikkate değer bir fark var: D'de parametreli türler T<X, Y, Z> yerine T!(X, Y, Z) olarak gösterilirler (T<X> yerine de T!(X) veya daha kısaca T!X kullanılır). C++'da bu konuda açılı parantezlerin seçilmiş olması; <, >, ve >> işleçleriyle çakıştığı için büyük gramer sorunlarına yol açmıştır. Böyle belirsiz durumları gidermek için de rasgele kabul edilebilecek kurallar gerekmiştir. Hatta dünyanın en az bilinen söz dizimi kuralı da bu şekilde ortaya çıkmıştır: nesne.template fonksiyon<arguman>(). Süpermen düzeyinde iyi C++'cı olan bir arkadaşınıza o söz diziminin ne anlama geldiğini sorun; arkadaşınızın büyük olasılıkla Kriptonit kullanmak zorunda kaldığını göreceksinizdir. Açılı parantezler Java ve C#'ta da kullanılmaktadır fakat o diller aritmetik ifadelerin parametre olarak kullanılmasına izin vermezler; ama sonuçta böyle bir olanağın ileride eklenme şansı da ortadan kalkmıştır. D bu konuda geleneksel birli ! işlecini ikili olarak kullanır ve parametreleri geleneksel parantezlerle gösterir (parantezleri hep doğru sayıda kapatıyorsunuzdur (değil mi?)).
Derleme modeli
D'de derleme, erişim denetimi, ve modül kavramlarının temeli dosyadır. Paketleme kavramının temeli de klasördür; bu konuda daha fazla karmaşıklık getirmez. Program kodunun daha gelişmiş bir veri tabanında bulunmasına gerek görülmez. D'nin yaklaşımı olan dosya ve klasör kavramı da zaten bir tür veri tabanıdır. En iyi programcılar tarafından uzun emekler sonucunda geliştirilmiş olan dosya ve klasör modeli sayesinde şu araçları da hazır olarak kullanma şansımız doğar: sürüm denetimi [version control], yedekleme, işletim sistemi düzeyinde koruma, günlükleme [journaling], vs. Böylece program geliştirmeye başlamak için gereken araçlar da ikiye inmiş olur: metin düzenleyici ve derleyici. Aslında D'de araçlar konusunda henüz fazla gelişme kaydedildiğini söyleyemeyiz ama yine de şunlar mevcut: Emacs'te d-mode modu, Eclipse'in Descent eklentisi, Linux'ta hata ayıklayıcı ZeroBugs, ve Poseidon geliştirme ortamı...
D'de kod üretimi bildiğimiz derleme ve bağlama adımlarından oluşur ama benzerlerinden çok daha hızlıdır; bunun iki, hayır üç nedeni var. Birincisi; dilin grameri sözcükleme [lexing], ayrıştırma [parsing], ve çözümleme [analysis] adımlarını birbirlerinden ayrı ve çok hızlı olarak yapmaya olanak verir. İkincisi; başka derleyicilerin çoğunda olduğu gibi program parçalarını [object file] ayrı ayrı oluşturmak yerine, D derleyicisine herşeyi önce bellekte oluşturmasını ve en sonunda diske yazmasını söyleyebilirsiniz. Üçüncüsü; D'nin yaratıcısı ve ilk gerçekleştiricisi olan Walter Bright, en iyileme [optimization] konusunda son derece deneyimli ve uzman birisidir. Geliştirme aşamalarındaki beklemelerin az olması D'yi çok güçlü bir yorumlayıcı [interpreter] haline de getirir (#!/bin/sh yazımındaki gibi #! bile desteklenir).
D ayrı ayrı derlemeyi destekleyen gerçek bir modül sistemine sahiptir. Modül özetlerini (başlık dosyalarının entel ismi) kaynak dosyalarından otomatik olarak öğrenebilir. Böylece başlık dosyalarıyla kendimiz ilgilenmek zorunda kalmayız; ama istersek bizim yazmamıza da izin verilir; ve bu konunun şikayet edilecek tarafı da kalmamış olur.
Bellek modeli ve çok çekirdekliler [manycores]
C fonksiyonlarını doğrudan çağırıyor olması D'nin de C bellek modeli üzerine kurulu olduğunu düşündürmüş olabilir. Öyle olabilse gerçekten iyi olurdu ama ne yazık ki bu, paralel mimarileri ile yüksek işlem gücü sağlayan çok çekirdekli işlemciler yüzünden olanaksızdır. Çok çekirdekliler artık günlük hayatımızdalar; C'nin bu konudaki yaklaşımı ise ne yazık ki çok sıradan ve hataya açık olarak kalmıştır. Yordamsal ve nesne yönelimli bazı başka diller ise bu konuda ancak pek az gelişme gösterebilmişlerdir ve bu durum, paralel işlemlerle ilgili sorunları değişmezlik [immutability] kavramının yardımıyla aşan fonksiyonel dillerin tekrardan gündeme gelmesine neden olmuştur.
Yeni bir dil olduğu için iş parçacıkları [threads] konusunda D çok şanslı bir durumdadır; D'nin bellek modeli diğer modellerden köklü farklılıklar içerir. İş parçacıklarıyla şu şekilde çalışmaya alışmışızdır: iş parçacığı başlatmak için bir fonksiyon çağrılır ve bu yeni iş parçacığı bütün programın belleğini görebilen ve değiştirebilen bir duruma geliverir. Veya seçime bağlı olarak, işletim sistemine bağlı bazı yöntemler kullanılarak iş parçacığına özel [thread-private] bellek de kullanılabilir. Şimdiye kadar yalnızca bir sorun olarak görülen bu durum, günümüzde bir kabus halini almıştır. Dünün sorunları, verinin eş zamanlı olarak değişmesinin getirdiği doğal bir sonuçtu: verinin hep geçerli bir durumda olmasını sağlamak amacıyla bütün değişikleri takip etmek, ve değişikliklerin doğru sırada yapılmalarını hedeflemek son derece güç bir iştir. Ama insanlar yine de bu durumu kabul etmek zorundaydılar; çünkü paylaşımlı bellek, donanımı çok sadık olarak modellemekteydi ve doğru çalıştığında çok verimli olmaktaydı. Şimdi kabus konusuna geliyoruz: günümüzde bellek eskiden olduğundan daha az paylaşımlıdır. Bugünkü donanımlarda işlemciler belleği daha katmanlı bir şekilde kullanmaktadırlar; her bir çekirdeğin kendine özel bir belleği vardır! Sonuçta paylaşımlı bellek yalnızca zor yöntem olmakla kalmamış, yavaş olan yöntem haline de gelmiştir; çünkü paylaşımlı bellek, artık donanımı sadık bir şekilde modellememektedir.
Geleneksel diller bu tür sorunlarla uğraşırken, fonksiyonel diller bu konuya matematiksel saflıkla yaklaşıyorlardı: donanımı modellemekle değil, gerçek matematiği modellemekle ilgileniyorlardı. Matematik çoğunlukla değişim [mutation] içermediğinden ve zamandan bağımsız olduğundan, paralel işlemler için çok uygundur. (Matematikçilikten dönme o ilk programcıların paralel işlemeyi ilk duyduklarında nasıl sevinmiş olabileceklerini hayal edebiliyorum.) Böyle bir modelin sırasız ve paralel işlemlere ne kadar uygun olduğu fonksiyonel programcılıkta başından beri biliniyordu. Ama bu uygunluk daha çok saklı bir enerji olarak duruyordu; günümüzde olduğu gibi bir amaç olarak görülmüyordu. Paralel işlemler kullanan ciddi programların en azından bazı bölümlerinin fonksiyonel ve değişimden bağımsız olarak yazılmalarının önemi günümüzde daha iyi anlaşılmaktadır.
Bu konuda D'nin aldığı tutum nedir? D'nin paralelliğe yaklaşımı temel bir kavram üzerine kuruludur: Belleğin öncelikle iş parçacığına özel olduğu varsayılır, ve ancak istendiğinde paylaşımlı olarak kullanılır.
D'de bütün bellek, hatta globaller bile, öncelikle onu kullanan iş parçacığına özeldir. Paylaşılmak istenirse nesneler shared anahtar sözcüğü ile bildirilirler ve ancak ondan sonra başka iş parçacıkları tarafından da görülebilirler. Burada önemli olan, tüm sistemin shared olan nesnelerden haberli olması, ve o nesnelere erişimin doğru sırada yapılmasını sağlamak için bazı kısıtlamalar getirmesidir. Bu model, nesnelerin paylaşımlı olduklarını varsayan dillerdeki bir sürü belalı sorunu böylece ortadan kaldırır. O dillerde nesnelerin hangilerinin paylaşıldıkları bilinmediği için programcıya güvenilmekte ve programcının nesneleri doğru olarak bildirmiş ve kullanmış olması beklenmektedir. Ondan sonra da çeşitli durumları açıklamak için karmaşık kurallar gerekmiştir: paylaşılmayan veriler, paylaşımlı olarak bildirilmiş olan veriler, paylaşımlı oldukları bildirilmediği halde paylaşımlı olan veriler, ve bunların her türlü kombinasyonu... Bu kurallar da onları anlayabilen topu topu beş kişi için gayet açıkça yazılmıştır ve geri kalanlarımıza da pes etmekten başka çare kalmamıştır.
Çok çekirdekliler konusunda araştırma ve geliştirme çabaları yoğun olarak devam ettiği halde hâlâ iyi bir model bulunamamıştır. Ama özel bellek modeli üzerine kurulu olduğu için D'nin bu konuda önü açıktır: saf fonksiyonlar, kilitsiz nesneler [lock-free primitives], alışık olduğumuz kilitlere dayalı programlama, mesaj kuyrukları (henüz plan aşamasında), vs. Sahiplenme türleri gibi daha ileri konular da halen tartışılmaktadır.
Değişmezlik [Immutability]
Buraya kadar tamam... Pekiyi matematiğin saflığı, değişmezlik, ve fonksiyonel kodlama gibi konulara ne oldu? D, fonksiyonel programlamanın ve değişmezlik kavramının paralel programlamadaki (ve başka konulardaki) önemini kabul eder ve hiçbir şekilde değişmeyecek olan nesneleri bildirmek için immutable anahtar sözcüğünü getirir. Aynı zamanda, D çoğumuzun yakından tanıdığı değişkenlik kavramını ve bu kavramın en iyi çözüm olduğu durumların varlığını da kabul eder. (Eğer siz çoğumuza dahil değilseniz, bazılarımız demiş olayım.) D'nin bu soruna yanıtı ilginçtir, çünkü böylece değişken ve değişmez nesneler kusursuzca bir araya getirilmiş olurlar.
Değişmez veriler neden bu kadar yararlıdır? Değişmez verilerin iş parçacıkları tarafından paylaşılmalarında erişim sırasının [synchronization] önemi ortadan kalkar. Doğal olarak da, hiç yapılmayan sıralama en hızlı sıralamadır... Buradaki ustalık, salt okunan [read-only] verilerin gerçekten yalnızca okunduklarını sağlamaktadır; yoksa zaten bu konuda hiçbir güvenceden söz edemeyiz. D, paralel programlamanın bu önemli özelliğini sağlayabilmek için fonksiyonel programlamaya eşi görülmemiş derecede destek verir. immutable olarak bildirilen verilerin değişmezlikleri derleme zamanında belirlenir; ve doğru yazılmış olan bir program immutable veriyi kesinlikle değiştiremez. Dahası, değişmezlik derinlemesinedir: değişmez bir alan içerisinden referansla geçtiğiniz başka bir alan da değişmezdir. (Neden? Çünkü öyle olmadığı zaman bütün sistem zayıflar: değişken verileri farkında olmadan paylaşmaya başlarız ve önlemeye çalıştığımız karmaşık kurallarla tekrar karşı karşıya geliriz.) Birbirlerine bağlı nesnelerin oluşturdukları alt grafların tamamı kolaylıkla immutable olarak bildirilebilirler. D'nin tür sistemi bunları tanıdığı için iş parçacıkları tarafından paylaşılmaları sağlanır ve tek işli [single-threaded] programlar için de eniyileme olanakları doğmuş olur.
Pekiyi özel bellek modelini benimseyen ilk dil D midir? Hiç de değil. D'yi diğerlerinden ayrı kılan, değişken ve değişmez verileri öncelikle özel olarak kabul ettiği tek bir bellek sistemi altında barındırmasıdır. Aslında bu konuda anlatacak çok ayrıntı var; biz yine tanıtıma devam edelim.
Güvenlik ön planda
Bir sistem dili olması nedeniyle D, son derece verimli ama bir o kadar da tehlikeli olanaklar sunar: denetimsiz işaretçiler, programcının elle yapabildiği bellek denetimi, ve en dikkatli tasarımları bile mahvedebilecek tür dönüşümleri. Bununla beraber, modüllerin güvenli olduklarını bildirebilme olanağı ve o olanağı desteklemek için bellek güvenliği sağlayan ve SafeD olarak adlandırılan bir derleme şekli de sunar. Yine de bu şekilde başarılı olarak derlenmiş olması; ne kodun taşınabilir olduğunu, ne güvenli olarak programlandığını, ne de birim testler [unit testing] gerektirmediğini gösterir. SafeD yalnızca bellek hatalarının [memory corruption] olasılığını azaltır.
Böyle güvenli modüller (veya güvenli derleme); işaretçileri olduklarından farklı kullanma ve yığıt nesnelerinin adreslerini fonksiyonlardan döndürme gibi bütün tehlikeli dil olanaklarını daha derleme zamanında önler. SafeD'de bellek hatalarıyla karşılaşamazsınız. Aslında bütün büyük programların kodlarının çoğunluğu böyle güvenli modüllerden oluşmalıdır; bu programların sistem modüllerinin az sayıda olmasına çalışılmalı, ve sistem modüllerinin kod incelemeleri [code review] çok dikkatlice yapılmalıdır. Çoğu program tamamen SafeD ile yazılabilir, ama tabii bellek ayırma gibi işleri halleden bazı modülleri yazarken biraz el emeği de gerekecektir. Böylelikle, programın bazı bölümlerini başka bir dilde yazmak gerekmemiş olur. SafeD henüz hazır değil; bu yazı yazıldığı sırada SafeD'nin gelişimi halen devam etmekteydi.
Kabul, başka yollar da olabilir
D'nin çok paradigmalı olduğunu söylerken, bazı seçimler sırasında çatışmaların gerekmeyebileceğini kastediyoruz. ["It doesn't have an axe to grind."] D bu konuyu kavramıştır... Herşeyin nesne, fonksiyon, liste, eşleme tablosu, veya Noel Baba olması gerekmez. Ne oldukları size kalmış olmalıdır. Bu yüzden D programcılığı özgürlükler getirir, çünkü bir sorunu çözmeye çalışırken eldeki olanakları o çözüme uydurmak için çabalamak gerekmez. Buradaki bir gerçeği de dile getirmek gerekiyor: özgürlüklerle birlikte sorumluluklar da gelir; bu sefer de çözüm için hangi tasarımın daha uygun olacağına karar vermek için zaman harcamak gerekecektir.
D bu konuda C++'nın izinden yürür; doğru olan tek yol gibi bir ısrarı yoktur. Ek olarak, D her paradigmaya daha fazla destek verir, paradigmalar arasında daha fazla uyum sağlar, ve seçilen paradigmaların izlenmesinde daha az engel çıkartır. Burada aslında iyi bir öğrenciyle karşı karşıyayız; D C++'tan çok şey öğrenmiştir. Daha az seçici diller olan Java, Haskell, Eiffel, Javascript, Python, ve Lisp'ten de etkilenmiştir. (Aslında çoğu dil Lisp'ten bir şeyler almıştır ama bazıları bunu itiraf edemez.)
Alt bölümde devam ediyor...