Pascal Costanza'nın Çok Dik Başlı Lisp Rehberi

http://p-cos.net/lisp/guide.html
(v1.41, 9/8/2005, changelog.txt, eski sürüm
Korece çeviri: http://www.cesian.com/lisp.kr.html

Copyright © 2002, 2004, 2005 Pascal Costanza. Tüm hakları saklıdır. Bu çalışmayı değiştirmeden ve bütünüyle kopyalama, yayınlama ve saklama izni verilmiştir.

Bu belgeyi online başvuru kaynağı olarak ya da yazdırarak kullanabilirsiniz. Tüm bağlantılar açık URL şeklinde belirtilmiştir, yazdırıldıklarında da korunurlar. Lütfen her türlü geribildiriminizi pc@p-cos.net adresine yollayın.



Bölüm I: Ön bilgi

Hep bir oduncu olmak istemişimdir!
- Monty Python

1. Niçin bu tanıtımı yazıyorum?


Bu bölümü aslında Ağustos 2002'de yazmıştım. Aşağıda tasvir ettiğim durum artık geçerli değil ama bu rehberin ruhunu korumak için zaman kipini değiştirmemeye karar verdim.
Şu anki durumum şöyle. Son yedi yıldır, çoğunlukla Java programlama diline uzantılar geliştirilen projelerde, Java programlama diliyle çalışıyorum. Bundan önce, daha çok Wirth ailesinden diller (çoğunlukla Modula-2 ve Oberon) kullanıyordum, yani başta Java'nın sonraki dillere göre sahip olduğu bazı avantajlardan bir hayli memnundum.

Geçen sene içerisinde, Java'nın hala çok (aslında aşırı) sınırlı bir programlama dili olduğunun farkına vardım ve alternatifler aramaya başladım. Feyerabend projesiyle olan alakam nedeniyle ( http://www.dreamsongs.com/Feyerabend/Feyerabend.html), Lisp, doğal olarak alternatifler arasında göründü. (Richard Gabriel, Feyerabend projesini başlatmıştı ve aynı zamanda 1980'lerin başında Common Lisp standartlaştırmasını başlatan insanlardan biriydi.)

Bir sürü güzel programlama dili bulunmasına rağmen (mesela, alfabetik sırada: gbeta, Objective CAML, Python, Ruby), hemen Lisp'in, bir bakıma tüm dillerin anası olduğu izlenimini edindim. Bu düşüncemin temel nedeni, Lisp'in kod ve veriye birörnek davranan tam bir hesaplama teorisi içermesi. (Bu "sadece" Turing-tam * olmaktan daha güçlüdür. Daha fazla bilgi için, aşağıdaki "Lisp'in Teknik Geçmişi" bölümüne bakın.) Bunun uygulamadaki anlamı; Lisp'te hiçbir (kavramsal) kısıtlama filan yoktur: eğer bir şeyi herhangi bir dilde yapabiliyorsanız, Lisp'te de yapabilirsiniz. Dahası Lisp, tüm türleri çalıştırma anında denetler, böylece hiçbir statik tür sistemi yolunuza çıkamaz.

Bu gözlemleri genelleyip şunu söyleyebilirsiniz: Lisp'in mantığı iddia eder ki, ifade edebilme gücü, bir programlama dilinin yegane önemli özelliğidir. O gücü kullanmak istediğinizde hiçbir şey yolunuza çıkmamalıdır. Bir programlama dili dünya görüşünü programcıya dayatmamalıdır. Programcı, dili kendi ihtiyaçlarına göre uyarlamalıdır, tersi olmamalıdır.

Bunun benim üzerimde büyük bir çekiciliği var ve bu yüzden Lisp'in seçeceğim dil olmasına karar verdim. Yine de, Lisp'e daha ciddi olarak bakmaya karar verdiğinizde bir kusur var: bu dil için Internet üzerinde, benim ihtiyaçlarıma uyan, gerçekten çok iyi rehberler yok - ya da en azından ben bulamadım. Elbette bazı rehberler bulunuyor ama ya çok alt düzeydeler ve çok yeni başlayanlara programlamayı öğretiyorlar, ya da çok akademikler ve sadece teorik olarak ilginç konulara değiniyorlar. Benim görmeyi istediğim şey ise size kavramları anlamanız için yeterli önbilgiyi sağlayan ve ardından sizi olabildiğince çabuk ilerleten bir rehber. ("Uzman programcılar için Lisp" gibi bir şey.)

Bir şey bulamadığım için, kendi kendime elimdekilerle çalışmak zorunda kaldım. Şimdi, diğer insanlar için işleri kolaylaştırmak adına, keşfettiklerimin bir özetini sunmak istiyorum.


2005 yılı itibari ile durumun dramatik olarak değiştiğini ve ilerleme kaydettiğini belirtmektefayda var. Peter Seibel, 2005 Nisan tarihinde yayımlanan bir Common Lisp kitabı yazdı. Kitabı aynı zamanda ttp://www.gigamonkeys.com/book/ adresinde de bulabilirsiniz. Bir hayli pragmatik bir yaklaşım sergileyen kitap "modern" kitleyi daha çok ilgilendirebilecek konular sunuyor: spam filtreleri, MP3 fonksiyon kitaplıkları ve HTML üretme. Bunun dışında Lisp büyümeye ve yaygınlaşmaya devam ediyor bu da değişik kanallardan erişilebilen bilgi üzerinde çok olumlu bir etkiye sahip.
Neden bu rehbere "çok dik başlı" deniyor? Lisp karmaşık bir dil, daha doğrusu diller ailesi. Lisp için "tek mutlak rehber" olamaz. Benim burada sunduklarım, ilk sırada görmeyi isteyeceğim şeyler. Diğer insanlar muhtemelen farklı bir yaklaşımı tercih ederler. Herkes için her şeyi anlatan bir rehber yazmak istemiyorum. Sadece benim gibi insanlara yararlı olabilecek şeyleri sunmak istiyorum. En azından bende işe yarayan bu kaynak, belki diğer insanlara da aynı şekilde uyabilir.

Özellikle bu, "Aptallar için 21 günde Common Lisp" gibi bir şey değil. Gerçek "Common Lisp deneyimi" hissini yakalayabilmeniz için, burada ya da başka bir yerde sunulan kaynakları birkaç kere yinelemeniz gerekebilir. (...herhalde herhangi ciddi bir dile hükmetmek için 21 günden çok daha fazlasına ihtiyacınız olduğunu vurgulamama gerek yok, değil mi? ;)

2. Genel tanıtım

Lisp'in uzun bir hikayesi var. 1950'lerde yaratıldı ve onlarca yılda sürekli geliştirilip, iyileştirildi. Çeşitli aşamalarda Lisp'in değişik lehçeleri oldu. Bu yüzden Lisp aslında tek bir dil değil, bir diller ailesidir. (C, C++, Objective C, Java ve C# gibi dilleri "C-benzeri" diller ailesinin üyeleri olarak düşünürseniz, bunu anlayacaksınız.)

1980'lerde ve 1990'larda, iki ana lehçe kendilerini yaygın olarak bulunan ve kullanılan lehçeler olarak kabul ettirdi: Common Lisp ve Scheme. Scheme, 1970'lerde Gerald Jay Sussman ve Guy L. Steele tarafından, nesne-yönelimli programlamayı anlamak ve Lisp'e dahil etmek için uğraşırlarken yaratıldı. Scheme, kavramsal seviyede (nesnelerin bir genellemesi olarak) "sözcüksel kuşatmalar"ı [1] ve (fonksiyon çağrımı ve mesaj iletiminin genellemesi olarak) "devam edişler"i [2]; teknik seviyede de kuyruk çağrılarının ve kuyruk özyinelemelerinin [3] aşırı optimizasyonunu ortaya çıkardı. Scheme'in iyi bir yanı da, daha çok gerçek ve güzelliği (diğer bir deyişle; minimalite ve ortogonalite) arayan akademisyenlere hitap eden özellikleri olan güzel bir mücevher olması. Fakat, günlük programlamada ihtiyacınız olan bir çok pratik şey eksik ve bu, kavramsal güzelliği berbat edebilir.


Yukarıda sözü geçen bazı terimlerin açıklamalarını merak ediyorsanız, aşağıdaki bağlantılara bakın.
1970'lerin sonunda, Lisp'in birçok türü kullanımdaydı. Common Lisp, bu abartılmış çeşitliliğe bir çare olarak başlatıldı - o zaman var olan çeşitli Lisp'lerin faydalarının birleştirilip, dezavantajlarının giderildiği, birleşmiş bir Lisp versiyonu. Dahası, Common Lisp, ileri doğru atılmış büyük bir adımdı çünkü Scheme'den öğrenilen dersleri de göz önünde bulundurmuştu. (Sözcüksel kapsam Common Lisp'e girdi. "Devam edişler" katılmadı çünkü pratik kullanım için çok karmaşık oluyorlardı. Kuyruk özyinelemelerinin optimizasyonu standartlaşmaya ihtiyaç duymadı çünkü bunu gerçekleştirme tekniği olarak kullanmak doğal bir ilerlemeydi.)

Böylece bunlar, bugün Lisp'i kullanmayı düşündüğünüzde nelere bakmanız gerektiği hakkında size bir ipucu veriyor. Eğer pratik "gerçek-dünya" uygulamaları yapmak istiyorsanız, Common Lisp'i seçmelisiniz. Eğer daha çok akademik araştırma yapmak ve mesela, "devam ediş" konusunda deney yapmak istiyorsanız, Scheme'de de karar kılabilirsiniz. Her iki dil de bugün hala kullanımda ve kendi kullanıcı toplulukları tarafından güçlü olarak destekleniyorlar. (Lisp'in ayrıca Emacs Lisp ve Lush gibi diğer çeşitleri de bulunuyor ama bunlar daha çok sınırlı, alana özgü lehçeler.)

Ben bir çok nedenden dolayı Common Lisp kullanmaya karar verdim. Bazıları bu rehber boyunca ortaya çıkacak. Scheme'de hoşuma gitmeyen, onun işlerin nasıl yapılması gerektiği konusunda çok katı bir "doğrucu" dil olması. Common Lisp çok daha serbest geliyor.

Gene de, Scheme hakkındaki yayınlar ve rehberler Common Lisp için de uygulanabiliyor - örneğin, sözcüksel kuşatmanın ne olduğu - bu yüzden ilerideki bölümlerde Scheme'e bazı başvurular ekledim.

(Özünde, benim iletim şu: Common Lisp ya da Scheme'i seçmeniz önemli değil, bu sadece belirli ihtiyaçlarınıza dayanır. Bu yüzden sadece içgüdüsel hislerinizin sizi yönlendirmesine izin verin. Aslında iki dil de, hem araştırmada hem de "gerçek dünya"da kullanılmak için yeterince esnekler. Gene de yazımın büyük bölümü Common Lisp'le ilgili; bu yüzden Scheme'i tercih ediyorsanız, bilgi kaynağı bulmak için başka yerlere bakmalısınız. Yol göstericiler için aşağıdaki bağlantılar bölümüne bakın. Bir seçeneğin hoşunuza gitmediğinin farkında vardığınızda, bir de öbürünü deneyin.)

Belki de burası insanların Lisp ve Scheme isimlerini ne için kullandıklarını anlatmak için iyi bir yer. Geniş anlamda Lisp genellikle, Common Lisp, Scheme, (artık kullanılmayan) MacLisp, InterLisp, Franz Lisp gibi daha eski lehçeler ve bir de Arc ve Goo gibi yenilerini içeren diller ailesi için kullanılır. Dar anlamda, Lisp şu an yalnız ve yalnız Common Lisp için kullanılıyor ve bu da özellikle Scheme'i dışarıda bırakıyor! Lisp isminin ne kadar geniş ve/veya dar kullanıldığı, kullanan kişiye ve bağlama göre değişir. Bir ucunda genelde, geniş anlamda bile, Scheme'i dışarıda bırakan, diğer ucunda da Dylan ve hatta Python'ı dahil eden insanlardan oluşan geniş bir yelpaze var. (Ben şahsen serbest olmayı ve belirli bir bağlamda önem kazandığında açık olmayı tercih ediyorum.)


Bu kılavuzdan nasıl faydalanabileceğinize dair bir not: Bu belgeyi sırayla okumayı doğal hale getirmeye çalıştım. Elbette bölümleri atlamakta ve özellikle bölüm I ve II arasında gidip gelmekte özgürsünüz. Fakat, ilerideki bazı bölümler biraz Lisp kodu örnekleri görmüş olmanızı gerektirebilir. Bunun için bu yazıda yeterli yön gösterici var.

3. Common Lisp'in "yöneticilere yönelik özetleri"

Common Lisp'in neler sunduğu hakkında tek sayfalık iyi bir yazı http://www.lisp.org/table/objects.htm adresinde bulunabilir. Bu yazı, CLOS'in (the Common Lisp Object System - Common Lisp'in bir parçası) nesne yönetim özelliklerine odaklanıyor ama buna ek olarak Common Lisp'in fonksiyonel ve prosedürel özellikleri için de iyi bir özet veriyor.

http://www.lispworks.com/products/lisp-overview.html'de bulunan LispWorks'ün yazdığı The Common Lisp Language Overview bir başka iyi özet.

4. Bazı ilk engeller

Lisp'in geçmişi diğer programlama dillerininkinden farklıdır, özellikle Algol ve C ailelerininkinden. Bu yüzden Lisp topluluğu, değişik seviyelerde, farklı kavramlar ve adlandırmalar geliştirmiştir. İşte karışıklığı önlemek için bazı açıklamalar.
  • Lisp'in dil tanımında yer alan fonksiyonlarından bazılarının komik görünen isimleri var. Örneğin "car", bir listenin ilk öğesini almak için, "cdr" de kalan öğeleri (gerisini) almak için kullanılır. Listeler "cons" yardımıyla oluşturulabilir, "mapcar" liste üzerinde ilerlemek için kullanılır, vb. Bilhassa Algol ve Wirth aileleri dilleri genellikle Lisp tasarımcılarının niçin daha "doğal" isimler kullanmadıklarını sorgularlar; "first", "rest", "foreach" vb. gibi... En azından, bu isimlerle karşılaştığımda benim verdiğim ilk tepkilerden biri buydu (ve elbette bu tepkiyi veren sadece ben değilim).

    Lütfen doğallık hakkındaki önyargılarınızı unutun - Lisp'in, büyük ölçüde tarihsel sebeplerden dolayı, sadece farklı bir kültürü var. Örneğin, sadece ve sadece anadiliniz olduğu için hakkıyla İngilizce'nin de, sözgelimi, Fransızca'ya göre daha doğal bir dil olduğunu iddia edebilirsiniz. Lisp'in terimlerine alışmak, sadece bir ya da iki gün fiili kod yazmayı alır ve sonra bunlar sorun olmaktan çıkar. Common Lisp de alternatif isimler sunuyor, mesela "car" ve "cdr" için "first" ve "rest", böylece seçme şansınız var. Gene de, geleneksel isimler kullanan kodları okumaya hazırlıklı olmalısınız.

  • Aynı şey Lisp'in görünüşte aşırı parantez kullanımı için geçerli. Bu sadece Lisp stili girintilemeyi destekleyen iyi bir metin düzenleyicisi seçme meselesi, ardından parantezleri çabucak unutursunuz. Lisp'te doğru sözdizim için çok nadiren telaşlanacaksınız. Unutmayın ki sözdizimlerinden dolayı C benzeri dillerde ve Pascal tabanlı dilinde, küme parantezleri ya da begin/end anahtar kelimeleri (if, while, switch/case vb.) kontrol deyimlerinde yanlış kullanıldıklarında, sorun yaratabilirler. Ancak, bu dillerde uzman bir programcıysanız, tahminimce çok nadiren bu konularda sorun yaşarsınız. Aynısı Lisp sözdizimi için de geçerli. Mesele sadece ona alışmakta.

  • Bazı insanlar hala, Lisp'in veri yapısı olarak sadece listeler sunduğunu zannediyorlar. Bu düşünce onların çok eski Lisp lehçelerini düşünmelerinden ve Lisp'in "List Processing"'in (Liste İşleme) kısaltması olmasından kaynaklanıyor. Aslında Common Lisp, diğer dillerde yararlı bulduğunuz her şeyi sunuyor; struct'lar (C benzeri struct'lar), diziler, hakiki çok boyutlu diziler, nesneler (Smalltalk and Java'daki gibi alanlar ve metodlarla), karakter katarları... vb.

    Benzer biçimde, bazı insanlar hala Lisp'in yorumlanan bir dil olduğunu düşünüyorlar. Gerçek şu ki, Lisp'in tamamen yorumlayıcı sistem olan gerçekleştirmesi çok az bulunuyor. Tüm ciddi gerçekleştirmeler, doğrudan doğruya makina kodu üreten derleyiciler içeriyor. Böylece Lisp genellikle derleme zamanı ve çalıştırma arasındaki farkı ayırt eder. Bu fark örneğin makro programcılığında önem kazanıyor.

    Hala, bu açıdan, C ve çoğu Pascal lehçeleri gibi sözde "derlenen diller"e göre farklar var. Çoğu zaman, bir Lisp programını açıkça derleyip, ardından çalıştırmazsınız. Bunun yerine, Lisp kodunu, yazıldıkça derleyen (ÇN. on the fly) bir Lisp geliştirme ortamıyla etkileşime girersiniz. Bu nedenle Lisp prensipte, "tam zamanında" (ÇN. just-in-time) derlenen diller türüne yakındır. Lisp'in, örneğin (bir yere kadar aslında Lisp ailesi üyesi olarak görülebilen) Smalltalk'un aynısı olan bir etkileşimli yapısı vardır.

    En önemli şey ise, Lisp'in çok verimli bir programlama dili olmasıdır. Common Lisp üreticileri, mükemmel performansı sağlamak için yıllardır çok sıkı çalışıyorlar. (Dikkat edin; JBuilder, NetBeans, Eclipse gibi çağdaş Java ve benzeri IDE'ler, Lisp ve Smalltalk ile aynı düzeydeki etkileşim yönüne muntazaman ilerliyor - yani açıkça Algol ve C benzeri dillerin toplulukları tarafından dahi değerli görülüyor.)

    Lisp'in başarım özelliklerinin Java ve C++ ile karşılaştırıldığı yeni bir çalışma şu URL'de bulunabilir: Erann Gat, Lisp as an Alternative to Java, http://www.flownet.com/gat/papers/. (Bu çalışmadaki Java programcılarının ne kadar deneyimli olduklarına dair bir sorununuz varsa, daha fazla açıklama için lütfen http://www.flownet.com/gat/papers/ljfaq.html adresine bakın.)

  • Lisp'in özelliklerinde çoğu kez kullanılan bir terim, "form"dur. Bir form, Lisp sözdiziminin temel bir öğesidir. İşte bir örnek:

    (format t "Merhaba, Dünya")

    Bu, konsola "Merhaba, Dünya" yazdıran bir formdur. ("t" parametresi Common Lisp'te "doğruluk" için standart Boole mantık değeridir [4] ve "format" fonksiyonu tarafından "standart çıktı" için bir gösterge olarak yorumlanır.) Yani "form" terimi, diğer programlama dillerindeki ifade ve deyimlerin yerini tutar. (Bir Lisp fanatiği, Lisp'in deyimleri [5] değil sadece ifadeleri [6] olduğunu ama bu ayrımın uygulamada önemli olmadığını iddia edecektir. Diğer programlama dillerinde ifadeler ve deyimler arasındaki sınır, sözde "ifade deyimleri"ne (değerleri dikkate alınmayan ifadeler) izin verildiği için, belirsizdir. Aynısını Lisp'te de yapabilirsiniz ve Lisp değeri olmayan (ya da "tanımlanmamış değerli") ifadeleri bile tanımlar. Yani uygulamada, Lisp ve diğer diller aynı ayrımı yapıyor ve benzer yollar kullanıyorlar, sadece farklı "varsayılan davranışlar" gösteriyorlar.)

    Lisp'te ayrıca "özel formlar" vardır. Bu terimi anlamanız için, Lisp'te genellikle tüm formların, fonksiyonların uygulamaları olduklarını bilmeniz gerekir. Yukarıdaki örnek, "format" fonksiyonunun "t" ve "Merhaba, Dünya" parametreli bir uygulamasıdır. Aşağıdaki örnek, "foo" fonksiyonunun "bar" ve "goo" parametreli bir uygulaması olurdu:

    (foo bar goo) Buna rağmen, Lisp'te fonksiyon uygulamaları belirtmeyen formlar vardır. Bunlar ya makrolardır ya da "özel formlar"dır. Özel formlar, sadece çok kısıtlı yerleşik işlemler kümesini belirtir. (Bunlara Common Lisp gerçekleştirmesi tarafından "özel olarak" davranılır. Dikkat edin, yerleşik fonksiyonlar ve makrolar özel formlar değildir!) Formun bir fonksiyon formu, makro formu ya da özel bir form olduğu, her zaman onun ilk öğesinden anlaşılır. Çeşitli formlar arasındaki ayrımın bazı teorik ve ayrıca pratik ilintisi var ama bunlar hakkında önceden kaygılanmanıza gerek yok. Önemli hale geldiği zaman farkı anlayacaksınız ve ardından bununla baş etmenin yollarını kolayca bulacaksınız.

  • Lisp topluluğunda, küçük-büyük diller konusunda büyük bir çelişme vardı, bir dereceye kadar hala da var. Bu, diğer topluluklardan insanlara şaşırtıcı gelebilir (en azından benim için durum buydu). Peki, nedir sorun?

    Lisp'in güzel bir özelliği de yerleşik fonksiyonları, kullanıcı tanımlı fonksiyonlardan dikkat çekecek şekilde ayırmaması. İşte iki örnek:

    (car listem)
    (cure listem)


    car fonksiyonu Lisp'te önceden tanımlanmıştır, cure için ise standart bir tanım yoktur bu yüzden kullanıcı tanımlı bir fonksiyon olmalıdır. Önemli olan nokta ise, farkı sözdizimden göremezsiniz. Bunu aşağıdaki, Java programlama dilinden bir örnekle karşılaştıralım:

    synchronized (lock) {
      beginTransaction(...);
      ...
      endTransaction(...);
    }

    Java'daki synchronized deyimi, belirli bir nesneyle eşlemeli olarak kod öbekleri yazmanızı sağlayan, yerleşik bir özelliktir. Bu örnekteki karşılıklı işlem yapan (transaction) metodlar da (tahminen) bu karşılıklı işlem kurallarını uygulayan kod öbekleri tanımlıyorlar ama bunun Java programlama dilinin yerleşik bir özelliği olmadığını anında anlayabilirsiniz. (Evet, Lisp öbek-yönelimli (block-oriented) deyimler - pardon - formlar sunuyor; ve evet, Lisp'in yerleşik özelliklerine benzeyen kendi öbek-yönelimli formlarınızı yazmanız da mümkün.)

    Şimdi, küçük-büyük dil meselesini anlamak için bu farkı hesaba katmalısnız. Peki Java ya da benzer bir programlama dilini standartlaştırma ne demektir? İlk önce dili standartlaştırmanız lazım ve ayrıca kütüphanelerin bazılarını da standartlaştırmanız lazım. Yukarıda belirtildiği gibi, bu tip dillerde yerleşik özellikler ile kütüphaneler arasında gözle görülür bir fark var, bu yüzden açıkça bunlar iki farklı iş.

    Lisp dünyasında böyle değil. Çünkü yerleşik ve kütüphane özellikleri arasında gözle görülür bir fark yok, kütüphanelerin standartlaştırılması zorunlu olarak dil standartlaştırmasının bir parçası haline geliyor (ve bir anlamda tersi de geçerli).

    Dolayısıyla küçük-büyük diller meselesi, (Algol ve C benzeri terimlerle) küçük-büyük kütüphaneler meselesine indirgeniyor - ve gerçekten de bu konuda söylenebilecek başka bir şey yok!

    Böylece bundan şu sonuç çıkıyor: Scheme, sadece çok küçük bir standart kütüphane sunduğu için küçük bir dildir oysa Common Lisp bir sürü yararlı ve standart özellikler sunduğu için büyük bir dildir. Yine de, her iki dilin de en temeline bakarsanız, büyüklük bakımından aşağı yukarı eşitler. (Dahası, eğer "ciddi" Scheme gerçekleştirmelerine bakarsanız onlar da, standartlaştırılmış olmasalar da, zengin özellikli kütüphaneler sunuyorlar.)

    Ancak küçük-büyük diller meselesiyle alakalı olmayan, kavramsal farklar var. Yani Scheme ya da Common Lisp arasında seçim yapmak sadece dil büyüklüğü meselesi değildir.

  • Örneğin, bazen Scheme'in bir "Lisp-1" ve Common Lisp'in de bir "Lisp-2" - ya da Scheme'in tek hücreli, Common Lisp'in ise çok hücreli bir sistem olduğunu görürsünüz. Burada bahsedilen şey bir elde değişkenlerin değerleri, diğer elde de fonksiyon tanımlarının Scheme'de aynı yolla, Common Lisp'te ise farklı yollarla saklandığıdır. Bu aslında uygulama yönünden o kadar da önemli değil çünkü besbelli iki dilde de gerçek programlar yazabilirsiniz. İkisi de Lisp-1 ya da Lisp-2 olmanın sonuçlarıyla baş etmek için yollar sunuyorlar. Bunu, onunla gerçekten karşılaşıncaya kadar rahatlıkla yoksayabilirsiniz ve yine, bununla baş etmenin yollarını kolayca bulacaksınız.

    (Bir "Lisp-1" uygulaması belli bir programlama tarzını daha uygun kılıyor oysa bir "Lisp-2" uygulaması makro programlamayı daha az hata eğilimli yapıyor ve nesne yönetimli özellikleri daha uygun kılıyor. Tam ayrıntılar biraz tehlikeli. Teknik çıkarımları daha fazla öğrenmek isterseniz, bunları aşağıdaki çalışmada ayrıntılı olarak görebilirsiniz.

5. Lisp'in Tarihi

Lisp'in, Common Lisp ve Scheme'i de içeren, tüm geçmişinin kapsamlı olarak anlatıldığı çok iyi yazılmış iki çalışma dikkate değerdir. Hatta bazı devrelerde, bu iki çalışma adeta polisiye roman gibi okunuyorlar - tavsiye edilir!
İkinci çalışma Lisp için oldukça eksiksiz bir kaynakça da içeriyor.

(Lütfen bunları Herbert Stoyan'ın Lisp'in geçmişi hakkındaki diğer çalışmaları ile karıştırmayın. Bana göre hem okuması zor hem de tuhaf sonuçlar çıkarmanıza yol açan yazılardı.)

6. Lisp'in teknik geçmişi

Önceden belirttiğim gibi, Lisp koda ve veriye birörnek olarak davranarak, eksiksiz bir hesaplama teorisi içeriyor - ve bu Lisp'i güçlü kılan ana özelliklerden biri. Bunun tam olarak ne anlama geldiği hakkında iyi bir açıklama verebilecek iki kaynak öneriyorum. (Bunlar "meta-döngüsel yorumlayıcılar" (meta-circular interpreters) kavramıyla alakalılar - bu terim sizi korkutmasın, göründüğü kadar karmaşık değil.) Şahsi fikrim, Lisp'in gücünü gerçekten değerlendirebilmek için, en azından birincisini okuyun.

...ve bu arada, bu makale, Lisp'teki temel ("ilkel") formlar hakkında iyi de bir açıklama veriyor.

"The Roots of Lisp"i sevdiyseniz ve meta-döngüsel yorumlayıcılar hakkında daha çok şey bilmek istiyorsanız, aşağıdaki çalışmada derin bir tartışma okuyup, bunlarla neler yapabileceğinizi görebilirsiniz. Bunun yanında sözcüksel kuşatma, dinamik bağlama ve benzer şeyler hakkında yararlı bilgiler edineceksiniz.
(Bu çalışma arkadan başka bir çalışma geleceğini ima ediyor ama böyle bir şey henüz ne yazıldı ne de yayınlandı. O yüzden boşuna aramayın: alıştırma olarak açık kalan noktaları tamamlamaya çalışın. ;)

7. Mevcut gerçekleştirmeler

Common Lisp gerçekleştirmelerinin bir listesini http://lisp.tech.coop/Implementation adresinde bulabilirsiniz. Bunlardan sadece bazılarını çok yakından inceledim. Çoğu Lisp programcısı Lisp geliştirme ortamı olarak Emacs veya XEmacs'i tercih ediyor ama ben daha çağdaş IDE'leri tercih ediyorum, bu elbette benim arama alanımı kısıtladı. (Tartışmaya gerek yok, bu mantıksız ve tamamen öznel zevk meselesi. Emacs editörünü kullanarak bir IDE kurmaya hevesliyseniz, http://common-lisp.net/project/slime/ adresindeki SLIME'ı inceleyin. Ayrıca Peter Seibel http://www.gigamonkeys.com/lispbox/ adresinde hazır yüklü Emacs tabanlı geliştirme ortamı olan Lispbox'ı sağlıyor.)

Apple Macintosh (Mac OS X) için özel öneri: Macintosh için LispWorks, ücretsiz olarak indirilebilecek Personal Edition'ı ile, mükemmel bir IDE. Macintosh Common Lisp de çok iyi bir IDE ama LispWorks kadar Mac OS X'le uyumlu değil. Digitool'dakiler MCL'i 30 gün boyunca denemenize izin veriyorlar. CLISP, ECL, OpenMCL ve SBCL, Mac OS X için bulunuyorlar ama IDEden yoksunlar, ki bu benim için çok önemli. Franz, Inc. gelecekte Mac OS X için Allegro Common Lisp ile bir IDE sunacak fakat kesin bir planları yok, o yüzden nefesinizi tutmayın.

Ben düzenli olarak hem Macintosh için LispWorks hem de Macintosh Common Lisp kullanıyorum. Yine de, bu bir ürün tavsiyesi değil - lütfen kendi gereksinimlerinizi kendiniz belirleyin!

Bölüm II: Eklektik bir kendi kendine öğrenme rehberi

HİÇKİMSE İspanyol Engizisyonu'nu beklemiyor! Bizim ana silahımız sürprizdir...sürpriz ve korku...korku ve sürpriz... Bizim iki silahımız korku ve sürprizdir...ve zalim verimlilik... Bizim *üç* silahımız korku, sürpriz ve zalim verimliliktir...ve Papa'ya olan neredeyse bağnaz bağlılığımızdır... Bizim *dört*...hayır...Silahlarımızın *en üstünde*... Cephanemizin en üstünde...korku, sürpriz gibi unsurlar var....Tekrar meydana çıkacağım....
- Monty Python
Yazımın bu bölümünde daha çok, Common Lisp'in çeşitli yönlerini tanımanıza yardımcı olabilecek ipuçları ve kaynaklar veriyorum. (Yani, daha çok standart kütüphanelerinden bahsediyorum.)

8. Başvuru kaynakları

Günlük programlamada genellikle fonksiyon tanımlarına, sözdizim açıklamalarına... vb. şeylere bakmak için başvuru kaynağına ihtiyaç duyarsınız. Bazı insanlar kitapları tercih eder ve bazıları da online kaynakları.

Geçerli olan Common Lisp standardı, 1995'te çıkan ve Common Lisp için en güvenilir kaynak olan ANSI standardı olarak belirtilir. Fakat, okuması zordur çünkü esasen sadece özellikleri belirtir, mesela temel mantığı belirtmez.

1990'ların başında Guy L. Steele, Common Lisp standartlaştırması için bir rapor olan ve ANSI Common Lisp'e benzeyen "Common Lisp the Language" kitabının ikinci sürümünü düzenledi. (Bu sürüm 1980'lerdeki ilk sürümün üzerine geldi ve genellikle CLtL2 olarak anılıyor. İlk sürüm -sadece CLtL- artık kullanılmamalı.) CLtL2, ANSI Common Lisp'teki bazı özelliklerden mahrum olmasına rağmen -ayrıca bazı küçük farklar da var- genellikle başlangıç için önerilebilir çünkü salt spesifikasyon yerine iyi tanımlamalar da sunuyor. Bu, kaynağı anlamanızı bir hayli kolaylaştırıyor. Yine de, birtakım tanımların tam ayrıntılarını bulmanız için, ANSI özelliklerini elde bulundurmanız da önerilir. Dahası ANSI belgesi, çok yararlı bir terimler sözlüğü içerir.

Neyse ki hem ANSI Common Lisp hem de CLtL2 kitapları HTML (ve PDF/PS) belgesi biçiminde online bulunuyor ve hatta bilgisayarınızda kullanmak üzere indirilebiliyor (HTML sürümleri dahil). İşte bağlantılar: Eğer basılı kaynak tercih ediyorsanız, ulaşmak için biraz gayret sarfetmeniz gerekiyor. CLtL2 baskısı kalmadı gibi ama Ebay'den kullanılmış bir kopyasını bulmayı deneyebilirsiniz. Ayrıca Amazon gibi bazı (online) kitapçılarda da hala mevcut. http://www.bh.com/digitalpress/ adresindeki yayımcı sitesinden satın almayı da deneyebilirsiniz.

ANSI belgesi http://global.ihs.com/ adresinde mevcut fakat 1000'den fazla sayfadan oluştuğu için kullanışlı ve/veya yararlı olduğunu tahmin etmiyorum. Dahası, 400$ civarı fiyatıyla gerçekten pahalı.

Paul Graham'ın "ANSI Common Lisp"i (alta bakın) iyi bir kitap ama başvurma amacı için yararlı bulmuyorum. Genellikle ben basılı spesifikasyonları tercih ederim (Java dili spesifikasyonu gibi) ama Common Lisp olayında elektronik sürümler de iyi.

9. En temeller

Paul Graham'ın yazdığı, "ANSI Common Lisp" adında bir ANSI Common Lisp kitabı var. İyi bir parça ve şansımız var ki yazar ilk iki bölümü web sayfasına koymuş. Özellikle ikinci bölümü önerebilirim çünkü çabucak Common Lisp'te programlamanın ne olduğu hakkında uygulamalı deneyimler sunuyor.

Tarzını sevdiyseniz, o sayfadaki bağlantıları takip ederek kitabı satın almayı düşünebilirsiniz.

Yeni başlayanlar için bir başka harika kaynak da Peter Seibel'in "Practical Common Lisp" isimli kitabıdır, hem normal kitap olarak hem http://www.gigamonkeys.com/book/ adresinde e-kitap olarak mevcuttur.

Sonrasında aradığınız çoğu bilgiyi CLtL2 ve/veya ANSI spesifikasyonunda bulmak oldukça kolay olacaktır. Naçiz fikrime göre elde etmesi biraz zor olan bazı bilgiler için aşağıya bakın.
Bölüm II'nin kalan kesimlerini tamamen anlayabilmeniz için, şimdiye kadar "The Roots of Lisp" ( http://www.paulgraham.com/rootsoflisp.html) ya da "ANSI Common Lisp Chapter 2"yi ( http://www.paulgraham.com/lib/paulgraham/acl2.txt) okumuş olmanız veya başka yollarla Lisp koduyla en azından karşılaşmış olmanız gerekir.

10. Daha ileri temeller

Er ya da geç, daha küçük sorunlar içeren aşağıdaki sorunlarla ilgilenmeniz gerekecek.

  • Common Lisp değişik kombinasyonlarda, opsiyonel argümanlarla, "rest" argümanlarıyla ve varsayılan argümanlarla çalışan fonksiyonlar tanımlama imkanlarını sunuyor. Bunların nasıl tanımlanacağıyla ilgili tam bir spesifikasyon CLtL2 bölüm 5.2.2'de ("Lambda Expressions") ve ANSI spesifikasyonu bölüm 3.4'te ("Lambda Lists"), özellikle de bölüm 3.4.1'de ("Ordinary Lambda Lists") bulunabilir.

    (Lambda ifadeleri isimsiz fonksiyonlardır. İsimli fonksiyonlar, diğer programlama dillerindeki yordamlara, fonksiyonlara ve/veya metodlara benzerler. Lisp'teki isimsiz fonksiyonlar Smalltalk'taki öbeklere veya, bir dereceye kadar, Java'daki anonim iç sınıflara benzerler. Açıklamalar için CLtL2 bölüm 5.2 ("Functions") ve alt bölümlerine bakın.)

  • Common Lisp'te standart çıktı, genellikle format fonksiyonu aracılığı ile gerçekleştirilir. Öz olarak C'deki printf'e benzerdir (yine de printf hakkında kapsamlı bilgim olmadığını itiraf etmeliyim!). Format hakkında, hiçbir çalışmada iyi bir genel tartışma bulamadım, bu yüzden temel olarak CLtL2'deki ilgili maddeyi okumanız gerekli.

    CLtL2'da format, bölüm 22.3.3'te ("Formatted Output to Character Streams") anlatılıyor. ANSI spesifikasyonu bunu bölüm 22.3'te ("Formatted Output") kapsıyor.

  • Karakter katarları Common Lisp'te şu nedenlerle biraz karmaşıktır. Common Lisp, Unicode'un çıkışından önce standartlaştırılmıştır - fakat, ASCII veya başka sınırlı karakter kümelerinin yeterli olmayacağı zaten belliydi. Bu yüzden Common Lisp standardı karakter katarlarıyla ilgilenmek için, bazı sorunları somut gerçekleştirmelere bırakan, genel yöntemler belirtir.

    Genellikle bu bir problem olmamalı ama diğer programlama dilleriyle yazılan uygulamalarla etkileşime geçmek isterken problem olabilir. Allegro Common Lisp, CLISP ve LispWorks, Unicode desteği sunuyor, diğer gerçekleştirmeler, bildiğim kadarıyla, (henüz) sunmuyor. (Ancak bu bakımdan da durum çok hızlı değişiyor o yüzden belli bir Lisp gerçekleştirmesinin durumunu yakından takip edin. Elbette kendi kendinize de Unicode desteği gerçekleştirebilirsiniz - Lisp bu amaçlar için yeterince esnektir. ;-)

    Karakter katarları desteği ile ilgili daha fazla detaya ulaşmak isterseniz, seçtiğiniz Common Lisp gerçekleştirmesiyle sunulan dökümantasyonla bağıntılı CLtL2 ve ANSI spesifikasyonuna başvurun.

  • Dosya isimlerinin işlenmesi ortamdan ortama farklılık gösterir. Yine Common Lisp, somut işletim sistemi tarafından gerektiğinde, gerçekleştiricilerin boşlukları doldurmalarına imkan veren bu tür "framework"leri standartlaştırmıştır. Yani ayrıntılı bilgi edinmek için yine CLtL2, ANSI spesifikasyonu ve Common Lisp gerçekleştirmenizin dökümantasyonuna başvurmanız lazım. CL-FAD dosyalarla uğraşırken taşınabilir (portable) kod yazmanıza yardımcı olan bir fonksiyon kitaplığıdır. Detaylar için http://www.weitz.de/cl-fad/.

11. Makrolar

Common Lisp'in makro özelliği bu dilin en ilginç yanlarından biridir - salt fonksiyonlar yerine, kendi programlama dili yapılarınızı yazmanız için çok güçlü bir imkandır.

Makrolar, güçlü olmaları ve anlaşılmaları görünüşte zor olduğu için, genellikle ileri seviye bir konu olarak görülürler. Halbuki, ben bunları kavramsal olarak epey basit ve yazması kolay bulmuştum.

Makrolar hakkında çok iyi bir tanımlama ve bunların nasıl kullanılacağı, Paul Graham'ın "On Lisp" kitabında bulunabilir. Bunun baskısı tükenmiştir ama aşağıdaki URL'den ücretsiz indirilebilir. Size benden bazı uyarılar ve notlar.

  • Common Lisp'teki makrolar, C'deki makrolar gibi değildir! Anladığım kadarıyla, C'deki makrolar karakter katarlarını işlerler ve işlettikleri programın yapısıyla ilgili hiçbir şey bilmezler. Aksine, Common Lisp'teki makrolar, programların tam yapısında çalışırlar. Bu mümkün, yine, çünkü Lisp'te kod ve veri birörnek olarak işlenir. Yani bir program, bir formlar listesidir ve, tabii ki, makrolar herhangi diğer bir liste gibi formlar listesini de işleyebilirler.

    Eğer "makro" terimine içgüdüsel tepkiniz sizi vazgeçiriyorsa lütfen iki kere düşünün - bu sadece diğer, daha gelişmiş, programlama dilleriyle bağlantılı olarak oluşmuş önyargılardan kaynaklanıyor olabilir. ;-)

    (Bundan sonra C makrolarıyla ilgili herhangi birşey söylemeyeceğimden, "makro" terimini sadece Lisp makroları için kullanacağım.)

  • Makrolar, bağımsız değişken olarak kod kabul eden ve karşılığında yeni kod üreten fonksiyonlar olarak anlaşılabilirler. Bir makronun bir uygulaması derleme zamanında bulunduğunda, ilgili form makroya geçirilir ve sonuç bu formun yerine geçer. Derleme daha sonra yeni formla devam eder. (Bu işleme "makro açılımı" denir.)

    Şimdi "cure"u, car ile aynı olacak şekilde tanımlayan bir makro tanımlayalım:

    (defmacro cure (argument)
       (list 'car argument))

    Artık gördüğümüz her (cure a), derleme zamanında (car a)'ya dönüştürülür - makronun sonucu, makro uygulaması yerine asıl kodun kullanılmasını tanımlar.

    (Not: Bu örnek sadece gösterme amaçlıdır. Makroların kullanımı için iyi bir örnek değil: "cure" bir fonksiyon olarak daha iyi tanımlanırdı.)

    Burda görebileceğiniz, makroların Turing-tam olduğu gerçeğidir. Common Lisp'in tüm gücü, derleme zamanında makrolar içinde yeni kod üretmek için kullanılabilir. Makrolar, sonuçlarını belirlemek için, diğer makro açılımlarını ve fonksiyon uygulamalarını içerebilirler ve, elbette, yineleme ve/veya özyineleme, koşullu ifadeler, vb. dahi kullanabilirler. Oluşan kod dahi makroların daha fazla uygulamalarını içerebilir, bu oluşan kod tamamen fonksiyon uygulamalarından oluşuncaya dek tekrar eden "makro açılımına" yol açar.

    Yani, uygulamada, Common Lisp'teki makrolar, örneğin, C++'ta kullanılan şablon meta-programlama ile aynı amaç için kullanılabilir. Özellikle, Üretken Programlama için kullanılabilirler! (Üretken Programlama esasında, derleyicileri genel amaçlı programlama dilleri için kod üreten, alana özgü dillerin tanımı ile ilgilenir.)

  • Makrolar genellikle "backquote" (ÇN. ters tırnak) sözdizimi ile bağlantılı olarak ortaya çıkarlar. Ben makroları bu şekilde tanıtmanın, anlamayı gereksiz yere zorlaştırdığına inanıyorum. Aslında makroların Turing-tamlığı konusundaki bir önceki notu anladığınız zaman, kavramsal seviyede, aklınızda makroların ne olduğu hakkında tam bir fikre sahip olursunuz. Yine de makrolar ve backquote sözdiziminin kombinasyonu uygulamada bazı değerli, sinerjik etkiler sağlayabilir. Aşağıda backquote sözdizimini tanıtacağım ve daha sonra da makrolarla nasıl birleştiğini göstereceğim.

    Yukarıda bahsedildiği gibi, Lisp'teki formların çoğu fonksiyon uygulamalarıdır. Yani (car mylist), listenin ilk öğesini çekip mylist'in belirttiği listeye koyar. Bazen bir formu çalıştırmak istemeyebilirsiniz (onun bir fonksiyon uygulaması olarak yorumlanmasını istemeyebilirsiniz). Mesela (car (a b c))'de, (a b c) varsayılan olarak üç öğeli bir liste olarak görülmez, a isminde, b ve c parametreli bir fonksiyon olarak görülür. (a b c)'nin değerlendirilmesini önlemek için "quote" özel formunu kullanmanız gerekir; şöyle: (car (quote (a b c))). Şimdi, bu ifadenin tamamının değerlendirmesi "a"yı verir ((a b c) listesinin ilk öğesi). Dikkat edin, (quote ...) bir fonksiyon uygulaması değil, özel bir formdur (Lisp'in yerleşik bir özelliği, yukarıya bakın).

    "quote" Lisp'te çok sık kullanıldığından, quote sözdizimini kullanımı şu şekilde kısaltılabilir: (car '(a b c)) aynen (car (quote (a b c))) anlamına gelir. Bu arada quote, bir değişkenin değerlendirilmesini önlemek için de kullanılabilir. Lisp'te değişkenler (diğer programlama dillerinde olduğu gibi) sadece isimlerdir, yani eğer, örneğin, (car a) yazarsanız, "a" değişkeninin belirttiği listenin ilk öğesi alınır. Değişkenlerin değerlendirilmesini önlemek için, tahmin edebileceğiniz gibi, (quote a) ya da kısaca 'a yazabilirsiniz.

    Şimdi bazen değişik ifade ve/veya değişkeni değerlendirip, bir listede birleştirerek listeler yaratmak isteyebilirsiniz. Bunu Lisp'te yapmanın en basit yolu şöyledir: (list a (car b) c); a, (car b) ve c'yi değerlendirir, sonra da sonuçları tek bir listede birleştirir. Bazen sadece bazı "liste" özel formu için parametrelerin değerlendirilmesi gerekir. Bu, bazı parametrelerin önüne kesme işareti koyularak kolaylıkla şu şekilde yapılabilir: (list a (car b) 'c), a ve (car b)'yi değerlendirir, fakat c'yi değerlendirmez. 'c'nin değerlendirilmesi, c sembolünün kendisini döndürür, böylece sonuçta oluşan listenin son öğesinin c olması garantilenir.

    Bazen tırnak işaretli (değerlendirilmeyen) ifadelerin sayısı, değerlendirilmesi gereken ifadelerinkinden fazladır. "Backquote" sözdizimi işte burada devreye girer: varsayılan tutumu değiştirir ve hiçbirşeyin değerlendirilmemesi gerektiğini varsayar. İstisnalar belirlenmelidir. Yani şu örnek: `(a b c ,(car d)), (list 'a 'b 'c (car d)) ile tamamen aynı anlama gelir - a, b ve c sembolleri değerlendirilmez, ama (car d) ifadesi değerlendirilir. "Backquote" sözdiziminde, değerlendirilmesi gereken ifadeler/değişkenler virgül ile belirlenir. (Buna dikkat ederseniz, "quote" sözdiziminde benzetim yapılamaz: eğer ' yazarsanız, bunu izleyen ifadenin tamamı tırnak işareti içine alınır ve bunun bazı bölümlerinin istisna olarak değerlendirilmesini sağlayacak hiçbir sözdizimi yoktur.)

    Yani esasen "backquote" sözdizimi sadece, neyin değerlendirilip neyin değerlendirilmeyeceğinin varsayılan işleyişini tersine çeviren kolaylaştırıcı bir özelliktir.

    Şimdi, yukarıda verilen makro örneğini hatırlayalım.

    (defmacro cure (argument)
       (list 'car argument))

    "Backquote" sözdizimi hakkındaki tartışmadan, bunun şu tanıma eşdeğer olduğunu çıkarabilirsiniz.

    (defmacro cure (argument)
       `(car ,argument))

    Artık "backquote" sözdiziminin makrolarla bağıntılı olarak niçin çok kullanışlı olduğunu hemen anlayabilirsiniz: makro tanımlamasının gövdesi artık gerçekte ürettiği koda çok daha yakın. (Hatırlayın, (cure mylist)'in bir uygulaması, derleme zamanında (car mylist) olarak neticelenir.)

    Bu arada, aşağıdaki kaynak, "backquote" sözdiziminin geçmişi hakkında çok iyi bir araştırma.

  • Makrolar, Common Lisp ve Scheme toplulukları arasındaki bir başka, daha gizemli bir çelişmenin tam kalbinde yer alırlar. Common Lisp'deki makrolar, istemeyerek, uygulamalarını çevreleyen kodun değişken isimlerini de yakalayabilirler. Bu aşırı tehlikeli gibi geliyor ama değil. Bu konu hakkında ayrıntılı bir tartışma için lütfen Paul Graham'ın "On Lisp" kitabına bakın - ben burada ayrıntılara girmeyeceğim.

    Scheme, değişken yakalamayı önleyen, "hijyenik makrolar" (ya da kısaca "hijyen") sözde kavramını öneriyor. Benim gördüğüm sorun; hijyenik makroları, makroların bir geliştirmesi olarak tanımlamak aşırı ölçüde kolay. Standart iddia şöyle: "Common Lisp makroları değişken isimlerini yakalayabilirler; hijyen bunu önler; böylece programlama daha güvenli olur." Bu iddia hijyenin, sözcüksel kuşatma kavramını belirli bir dereceye kadar anımsatması gerçeği ile bir şekilde destekleniyor.

    Fakat, tüm gerçek bu değil. Bazen hakikaten değişken isimlerini yakalamanız gerekir ve hijyen bunu gerektiğinden daha zora sokar. Dahası, Common Lisp'te isim yakalamayı önlemek gerçekten çok basit. Paul Graham bunu kitabında gösteriyor, o yüzden burada çözülmesi gereken çok ciddi bir sorun yok.

    Benim iletim şu: hijyen, Common Lisp'te gerekli değildir (ve de sunulmamıştır) çünkü güvenli makrolar yazmak kolaydır. Teorik seviyede bu konuyla gerçekten ilgilenmediğiniz sürece, hijyenik makrolar konusunu rahatlıkla görmezden gelebilirsiniz.

    (Scheme'deki durum biraz daha farklı çünkü Scheme değişkenleri ve fonksiyon tanımlamalarını aynı yolla depolar - bir Lisp-1 çeşididir, yukarıya bakın. Bu demektir ki, kazara fonksiyon tanımı yakalama olasılığı, Common Lisp'e göre daha fazladır. Bu yüzden, bu toplulukta bu sorunu çözmek için daha fazla ihtiyaç vardır. Olası makro hijyen problemlerini anlatan iyi bir çalışma:

Bu çalışmada verilen örneklerin neden "Lisp-2" ve aynı zamanda Common Lisp'te gerçekten o kadar önemli olmadığını ve hangi durumlarda gerçekten isimleri yakalamak isteyebileceğinizi anlamaya çalışmanız iyi bir alıştırma olacaktır. Yukarıda bağlantısı verilen, Gabriel ve Pitman'ın fonksiyon hücreleri ile değer hücrelerinin ayrımı konusu için de bu çalışmaya bakın.)

12. Nesne yönelimli özellikler (CLOS)

The Common Lisp Object System (CLOS),[7] ANSI Common Lisp'in bir parçasıdır. Fakat standarta geç eklendiğinden, bazı sağlayıcılar bunu gerçekleştirmek için kendi zamanlarını harcadılar. Şimdilerde, neredeyse tüm gerçekleştirmeler CLOS desteği sunmaktalar. (Bazı sağlayıcılar bunu kendi Common Lisp'lerinin seçkin bir özelliği olarak övüyorlar ve ben bunu bazen şaşırtıcı buluyorum. Açıkça söylemek gerekirse, bir Common Lisp sistemi, CLOS'in tam bir gerçekleştirmesini içermediği sürece, ANSI standardını gerçekleştirdiğini iddia edemez.)

CLOS sınıflar, alt sınıflama, çoklu kalıtım, çoklu metodlar ve ön, art ve anlık öneriler ile tam nesne yönelimi sunuyor. Bu, diğer nesne yönelimli programlama dilleri tarafından sunulan özelliklerin ötesindedir.

Dahası, çalıştırma sırasında sınıf sıradüzenini ve metod dağıtımını incelemek ve idare etmek için bir "Meta-Object Protocol" (MOP) eklendi. Meta-Object Protocol, ANSI Common Lisp'in bir parçası değildi ama çoğu CLOS/MOP gerçekleştirmeleri aynı kaynaklardan türetildiği için fiili bir standarta dönüştü. (CLOS ve MOP, Java dili ve yansıma API [8] gibi ayrı varlıklar değildirler ama MOP, CLOS'in bir alt kümesi görülebilir.)

CLOS ve Meta-Object Protocol için mükemmel bir açıklama, aşağıdaki çalışmada bulunabilir:

  • Linda G. DeMichiel and Richard Gabriel, The Common Lisp Object System: An Overview, ECOOP '87, Springer LNCS 276, http://www.dreamsongs.com/Essays.htmlsayfasının en altından ücretsiz indirilebilir (Richard Gabriel'a bunu ricam üzerine sağladığı için teşekkürler).


Bazı CLOS tasarım kararları için gerekçeler başka bir çalışmada bulunabilir:

  • Richard P. Gabriel, Jon L. White, and Daniel G. Bobrow, CLOS: integrating object-oriented and functional programming, Communications of the ACM, Volume 34, Issue 9, 9/1991, http://doi.acm.org/10.1145/114669.114671

Jeff Dalton, http://www.aiai.ed.ac.uk/~jeff/clos-guide.html sayfasında, CLOS için kısa bir rehber sunuyor (MOP'yi dahil etmeden). ANSI HyperSpec ile benzer stilde yazılmış bir MOP kaynağı http://www.lisp.org/mop/ adresinde bulunabilir. (Bu sayfadaki çoğu dış bağlantı çalışmıyor ama kaynağın kendisi çalışıyor.) Barry Margolin, comp.lang.lisp'te güzel bir pratik kural verdi:

  • ADD-METHOD ve ENSURE-GENERIC-FUNCTION gibi birkaç MOP fonksiyonu ANSI CL'e sızmayı başardı ama daha fazlası değil.

    "Bir şeyin CLOS'in mi yoksa MOP'nin mi temel bir parçası olduğunu anlamak için iyi bir yol, bağımsız değişkenlerinin sınıf, metod ya da genel fonksiyon olup olmadığına bakmaktır. MAKE-INSTANCE veya CHANGE-CLASS gibi fonksyionlar bu kategoriye girmezler; bir sınıf nesnesi kabul etmelerine rağmen, aynı zamanda yerinde sınıf ismi de kabul ederler ve MOP fonksyionları genellikle bu uygunluğu sunmazlar."

Meta-Object Protocol ile neler yapabileceğinizin ayrıntılı (ve etkileyici) bir örneğini, aşağıdaki çalışmada bulabilirsiniz:



Bazı ek notlar:

  • Çoğul kalıtım [9] bazı insanlar tarafından kötü bir şey olarak görülür çünkü çözülmesi zor potansiyel isim çakışmalarına yol açar. Çoğu programlama dili bu tür isim çakışmaları için varsayılan bir davranış sunar ama bu çoğunlukla yetersiz çözümlere yol açar. Common Lisp de, programcı tarafından verilen üst sınıfların sırasına dayanan topolojik bir sıralama yaratarak, isim çakışmaları için varsayılan bir strateji tanımlamaktadır. Elbette, birisi kolayca çıkıp, bu yaklaşımın yararlı sonuçlar vermediği patolojik örnekler gösterebilir. Yani görülüyor ki, Common Lisp'in diğer programlama dillerininki kadar kötü bir çözümü var.

    Fakat bu, CLOS'te, kullanıcı tanımlı "metod kombinasyonları" ile telafi edilmiştir. Bir programcı daima, her olay için metodların nasıl gönderileceğini açıkça tanımlayabilir. (Tabii ki, bu çalıştırma zamanında dahi değiştirilebilir.) Yani CLOS, isimlendirme çakışmalarıyla baş etmek için çok yüksek derecede bir esneklik sunuyor. Üst sınıfların topolojik olarak sıralanması ise sadece sistemin yaptığı bir öneri olarak anlaşılabilir.

    Çoğul kalıtımdan kaynaklanan isimlendirme çakışmalarıyla baş etmek, kavramsal nedenlerden dolayı, her programlama dilinde zordur. Yine Common Lisp, programcıya, ortaya çıkacak herhangi bir durumla baş edebilmek için gerekli tüm etkileyici gücü vererek ağır basıyor. Bunu, örneğin, ara yüzlerin çoğul kalıtımlarının hiçbir şekilde baş edilemeyen isimlendirme çakışmalarına yol açabileceği Java'daki durumlarla karşılaştırın.

  • Standart metod kombinasyonları ve Meta-Object Protocol, "aspect-oriented" programlamayı mümkün kılar. Bu bir tesadüf değil - Gregor Kiczales, "aspect-oriented" programlamanın yaratıcılarından, CLOS'in de tasarımcılarından biri. AspectJ'in gelecek haftaki sürümü için beklemeyin - şu an zaten Common Lisp ile her şeyi yapabilirsiniz! ;-)

13. LOOP imkanı

LOOP imkanı, Common Lisp'in, döngüleri Algol/Wirth dillerine benzer tarzda ifade etmesini sağlayan başka bir standart özelliğidir. İşte on tane yıldız bastıran bir döngü örneği:

(loop for i from 1 to 10
      do (format t "*"))

Yine CLtL2 iyi bir bilgi kaynağı. Fakat hemen fark edeceksiniz ki, çok fazla sayıda olasılık ve çok farklı biçimlerde kombinasyonlar mevcut. Benim izlenimim, tüm ayrıntıları öğrenmenin mantıklı olmadığı çünkü bunun aşırı uzun süreceği şeklinde. LOOP imkanının amacı, açıkçası, bir şekilde "doğal" ve İngilizcemsi yineleme ifadeleri sağlamak içindi ve bunun kaynak kodu anlamayı kesinlikle kolaylaştırdığı örnekler mevcut. (Bu aynı zamanda, yinelemenin alanı için Common Lisp'e yerleştirilmiş alana özgü (domain specific) bir dil için güzel bir örnek.)

Aşağıda bir başka, daha da ilginç bir LOOP imkanı örneği (Matthew Danish'ten).

(defun fibonacci (n)
   (loop for a = 1 then (+ a b)
         and b = 0 then a ;  paralel olarak ilerliyor
         repeat n
         collect b))

Sanırım LOOP imkanını kullanmanın amacı, sadece, bir yinelemeyi ifade etmek için bir yol "tahmin etmek" ve çalışıp çalışmadığını görmek. Çalışmıyorsa, CLtL2 veya ANSI spesifikasyonlarına bakarsınız ya da yinelemeleri veya özyinelemeleri ifade etmek için daha Lispimtırak yollara dönersiniz (do, dotimes, mapcar ve benzerleri ile).

Yine de bu sadece benim tahminim, LOOP imkanının tasarımcılarının gerçek niyetlerini bilmiyorum. (Bazıları, ayrıntıların çok çabuk öğrenilebileceğini öne sürüyorlar ama yine de bunu kendi başıma denemeliyim.)


Bu arada LOOP yapısını öğrenmeyi başardım ve epey kullanışlı bulduğumu söylemeliyim. Vakit alıyor ama deneyimlerime göre bu vakte değiyor. Peter Seibel kendi kitabında LOOP için başlıbaşına bir bölüm ayırmış, yine de pek çok durumda deneme yanılma şeklindeki tavsiyem yeterli olacak gibi görünüyor.

14. Koşullar

Koşullar, az çok, Java benzeri dillerdeki istisnalar gibidir. Fakat koşullar daha etkilidir çünkü koşul işleyiciler, örneğin, Lisp çalıştırma sistemini koşulun ortaya çıktığı yerden çalıştırmaya devam etmesi için yönlendirebilirler. Bu, istisna işlemeye alışkınsanız, ilk bakışta garip gelebilir. Hey, bir istisna her zaman bir sorun olduğu anlamına gelir değil mi? İşte, Common Lisp'te koşullar sorun olmadığı zamanlarda da bildirilebilir ama örneğin, çokişlemli (multithreaded) bir anuyumlulaştırılması (senkronizasyonu) ya da diğer bir kodun özel olaylardan haberdar edilmesi, ya da aklınıza gelen yararlı her konuda da bildirilebilir. Dahası, gerçekten sorunlar bildiren koşullar anında etkileşimli olarak ya da programlamayla düzeltilebilir.

Sonuç olarak, çoğu nesne yönelimli programlama dilindeki modellerin CLOS'un sadece özel bir durumu olmaları gibi, Java'nın istisna işlemesi de yine Common Lisp'in çok daha genel Koşul sisteminin özel bir durumudur.

Common Lisp'te koşul işleme hakkında, bazı ilginç tarihsel eğlencelikleri de içeren, mükemmel bir açıklama aşağıdaki çalışmada bulunabilir.



Bazı notlar:

Common Lisp, fonksiyonlardan yerel olmayan çıkışı sağlayan catch ve throw yapılarını da özellik olarak sunuyor. Bunlar Java'daki try-catch-finally üçlemesiyle benzer değildirler! Onun yerine, try-catch öbekleri yerine handler-case, handler-bind vb. ve try-finally öbekleri yerine de unwind-protect kullanılır. Ayrıntılar için lütfen yukarıdaki çalışmaya ve spesifikasyona bakın.

Sırası gelmişken, Koşul sistemi "Aspect Oriented" Programlama için de, meta programlamaya gerek kalmadan bir temel oluşturur!

15. İleri programlama teknikleri

Paul Graham'ın "On Lisp" kitabından, makro programlama bağlamında bahsetmiştim. Bu kitap yardımcı fonksiyonların yaratımı, yüksek derece fonksiyonlar, veritabanı erişimi, Scheme benzeri "devam ediş"lerin benzeştirimi, çoklu işlemler, deterministik olmama, ayrıştırma (parsing), mantıksal programlama ve nesne yönetimi gibi Common Lisp için bir sürü diğer ileri programlama teknikleriyle de ilgileniyor. (Yine de kitabın çoğu makrolarla ilgili).

Bağlantıyı rahat olsun diye tekrarlıyorum.

Paul Graham, On Lisp, http://www.paulgraham.com/onlisp.html.

16. Paketler

Java'nın, sınıfları paketler içerisinde toplamanızı ve bunların birbirlerinden bir dereceye kadar korunmasını sağlayan, güzel bir modül sistemi vardır. Dahası, paket yapısı muhtemelen zip -ya da jar- dosyalarında saklanmış olarak eşlenen dosyada -ve klasörde- yansıtılır. Bu kolay ama ayrıntılı bir şekilde, gerektiğinde Java çalıştırmasının sınıfları yükleyeceği yerlerin tanımlandığı sınıf yollarını işlemeye imkan sağlar.

Common Lisp de tanımların (elbette sınıfları da içeren) toplanacağı bir paket sistemi sunar. Fakat, hiçbir dosya -ve/veya klasör- yapısı ile eşleşme yoktur. Common Lisp'te paket sistemi sadece, tanımların çalıştırma anında Lisp ortamı içerisinde nasıl düzenlendiğiyle ilgilenir.

Aslında, Common Lisp'in paket sistemi bundan daha güçlüdür çünkü daha genel olarak sembolleri paketler içerisinde gruplamanıza izin verir. Çünkü Common Lisp'te tanımlamalara her zaman sembollerle erişilir, tanımlamaları paketlemek genel paket kavramının yalnızca özel bir halidir. İşte Common Lisp'te paketler ile başka neler yapabileceğiniz hakkında, Kent M. Pitman tarafından comp.lang.lisp'te verilmiş güzel bir örnek. (Aşağıdaki örneği içeren iletinin tamamını görmek için http://makeashorterlink.com/?E3B823F91 adresine bakın.)

  • "CL bana, basit sembol verileri şeklinde, değişik türlerde paylaşım yapıp yapmadığımı denetleme imkanı sunuyor. Mesela, yerimi aşağıdaki şekilde ayırabilirim:

    (in-package "FLOWER")
    (setf (get 'rose 'color) 'red)
    (setf (get 'daisy 'color) 'white)

    (defun colors-among (things)
      (loop for thing in things
            when (get thing 'color)
             collect thing))

    (in-package "PEOPLE")
    (setf (get 'rose 'color) 'white)
    (setf (get 'daisy 'color) 'black)

    (defun colors-among (things)
      (loop for thing in things
            when (get thing 'color)
             collect thing))

    (in-package "OTHER")

    (flower:colors-among '(flower:rose people:rose flower:daisy))
    => (FLOWER:RED FLOWER:WHITE)

    (flower:colors-among '(flower:rose people:daisy people:rose))
    => (FLOWER:RED)

    (people:colors-among '(flower:rose people:rose flower:daisy))
    => (PEOPLE:WHITE)

    (people:colors-among '(flower:rose people:daisy people:rose))
    => (PEOPLE:BLACK PEOPLE:WHITE)


Yani Common Lisp'in paket sistemi sembollerin toparlanmasıyla ilgilenir ve böylece "yan etki" olarak tanımlamaların toparlanmasını sağlar ama sistem parçalarının aranması ve yüklenmesiyle hiç ilgilenmez. Common Lisp terminolojisinde, ikinci göreve "sistem inşası" denir.

Yani lütfen bu konuda aklınız karışmasın. Java'nın paketleri ve Common Lisp'in paketleri sadece aynı isme sahiptirler ama farklı amaçlar için kullanılırlar (bazı ortak yanlarla birlikte).

Common Lisp sistem inşası için sadece temel destek tanımlar ("load" ve "require" fonksiyonları - CLtL2 ve ANSI spesifikasyonuna bakın). Lütfen bu konuda daha fazla bilgi için bir sonraki bölüme bakın.

17. Eksik olan ne?

ANSI Common Lisp standardı 1994'te sonuçlandırıldı ve 1995'te yayınlandı. Bu Java'nın köşeden göründüğü fakat henüz halka açılmadığı zamanlardı. Bu aynı zamanda Internetin ticari yükselişinden de önceydi. Şurası açık ki, son yedi senedir, programlama desteği birçok yönde ilerledi. Maalesef bu sürede Common Lisp'in "resmi" standartlaştırılması devam etmedi. ANSI Common Lisp'in içermediği birçok şey, şu anda diğer, daha moda programlama dillerinde mevcut. Bu özelliklerin en üstünde şunlar var: düzgün bir modül ("sistem inşası") olanağı, Unicode desteği, ortamdan bağımsız grafik kullanıcı ara yüzü kütüphanesi, soketler ve TCP/IP, XML, Ağ Hizmetleri ve benzeri (en çok sevdiğiniz özelliğinizi siz ekleyin).

Yine de, Lisp dünyası bu zaman zarfında yerinde saymadı - bir şeyin ANSI Common Lisp standardında belirtilmemesi, onun var olmadığı anlamına gelmez.

Yaygın kullanılan iki sistem inşa olanağı ASDF (http://www.cliki.net/asdf) ve MK-DEFSYSTEM'dir (http://www.cliki.net/mk-defsystem). Bunlara ilaveten, bazı Common Lisp gerçekleştirmeleri kendi sistem yorumlama destekleriyle birlikte geliyorlar. Allegro Common Lisp, LispWorks ve CLISP Unicode desteği sunuyorlar. CLIM; Franz, Xanalys ve Digitool gibi bazı dağıtımcılar tarafından desteklenen, platformdan bağımsız bir grafik kullanıcı ara yüzü kütüphanesi. En ciddi Common Lisp gerçekleştirmeleri soketler için destek ve daha ileri ağ bağlantısı imkanları sağlıyor. CLISP fastgci için destek sağlıyor. CL-XML, XML ile ilgilenmek için bir kütüphane.

Yani temel ileti şu: eğer kütüphaneye ihtiyacınız varsa Google'da biraz araştırma yapın, kullanabileceğiniz birşeyler bulabilirsiniz. Ayrıca aşağıda verilen bazı bağlantılara da bir göz atın.

Bölüm III: Bağlantılar

Televizyon ekranı aklın gözünün retinasıdır.
Bu yüzden, televizyon ekranı beynin fiziksel yapısnın bir parçasıdır.
Bu yüzden, televizyon ekranında görünen herşey onu izleyenler için ham deneyime dönüşür.
Bu yüzden, televizyon gerçekliktir ve gerçeklik televizyondan daha azdır.
- Videodrome'da Profesör O'Blivion , David Cronenberg'den


İşte yararlı ve/veya ilginç bağlantıların bir listesi. Hepsinden olmasa da bazılarından yukarıdaki yazıda zaten bahsedildi.

18. Kişisel web siteleri

19. Common Lisp Kaynakları

20. Altyapı bilgisi

21. Havuzlar, bağlantılar, yazılımlar

22. Scheme bağlantıları

23. Telif hakkı konuları

  • Online Yazarlar için Telif Hakkı Rehber, yazan Gene Michael Stover, http://lisp-p.org/copyright/
  • Teşekkürler

    Taslak sürümde bir çok yararlı geribildirim sağlayan (alfabetik sırayla) Tom Arbuckle (http://www.sqrl.ul.ie/staff.html), Joe Bergin (http://csis.pace.edu/~bergin/) ve Richard Gabriel'e (http://www.dreamsongs.com/) çok teşekkürler. Seung Mo Cho'ya, Korece çeviri için teşekkür ederim.

    Daha da fazla geribildirim için Paolo Amoroso, Marco Antoniotti, Tim Bradshaw, Christopher Browne, Thomas F. Burdick, Wolfhard Buß, Bill Clementson, Matthew Danish, Biep Durieux, Knut Aril Erstad, Frode Vatvedt Fjeld, John Foderaro, Paul Foley, Erann Gatt, Martti Halminen, Bruce Hoult, Arthur Lemmens, Barry Margolin, Nicolas Neuss, Duane Rettig, Dorai Sitaram, Aleksandr Skobelev, Thomas Stegen, Gene Michael Stover, Rafal Strzalinski, Raymond Toy, Sashank Varma ve Espen Vestre'ye ilaveten teşekkürler. (Çoğu comp.lang.lisp'te etkin katılımcılar)

    Çevirenin ve Editörlerin Notları

    Bu belge, özgün yazarın izniyle Hayrettin Gürkök tarafından çevrilmiş, son düzenlemeler (soyadı alfabetik sırasına göre) Seda Çelebican, Hayrettin Gürkök, Bülent Murtezaoğlu ve Emre Sevinç tarafından gerçekleştirilmiştir. Bazı Türkçe terimlerin karşılıkları ve açıklamalar aşağıda belirtilmiştir.

    [1] Lexical closures

    [2] Continuations

    [3] Tail recursions, bkz. http://en.wikipedia.org/wiki/Tail_recursion

    [4] Boolean

    [5] Statement

    [6] Expression

    [7] Common Lisp Nesne Sistemi

    [8] Reflection API

    [9] Multiple inheritance

    * Hesaplama teorisinde bir programlama sistemi ya da herhangi bir formel mantıksal sistem eğer evrensel Turing makinasına denk bir hesaplama gücüne sahipse, Turing-tam olduğu söylenir. Başka bir deyişle sistem ve evrensel Turing makinası birbirlerine öykünebilirler (emulate each other). Bkz. http://en.wikipedia.org/wiki/Turing_completeness