Common Lisp ile Internet Programlamaya Giriş Kılavuzu

0
FZ
Beklenen an geldi. Common Lisp kullanarak web programlamaya dair ilk makalemizi yayınlıyoruz. Giriş seviyesindeki bu makalede en temel bilgiler aktarılmış ve Lisp heveslilerinin gerekli araçları nasıl kuracakları ve ayarları nasıl yapacakları gösterilmiş, ilk bebek adımlarını atmaları amaçlanmıştır.

Makale, İstanbul Bilgi Üniversitesi, Bilgisayar Bilimleri Bölümü öğrencilerinden Haldun Bayhantopçu tarafından yazılmış ve Emre "FZ" Sevinç tarafından son düzenlemeleri yapılmıştır. Teknik konular ve teknik üslup konusunda eleştirilerini esirgemeyen Bülent Murtezaoğlu'na teşekkür ederiz.

Afiyet olsun...

Lisp ile Web Programlamaya Dalış

Lisp ile web programlamaya giriş niteliğinde olan bu yazıda, Debian GNU/Linux yüklü bir sisteme gerekli olan programları nasıl kuracağımızı gösterdikten "merhaba dünya" şeklinde ufak bir web uygulamasını nasıl geliştiriceğimizi, ardından da güçlü makro özellikleri ile neler yapabileceğimize bakacağız. Bu yazıda anlatacaklarımız, Debian GNU/Linux'un "testing" dağıtımı olan Etch isimli dağıtım üzerinde denenmiştir. Aynı adımları "stable" olarak nitelenen Debian Sarge ve "unstable" olarak nitelenen SID üzerinde de pek bir sorun çıkmadan takip edebileceğinizi düşünüyoruz. Unutmadan söyleyelim, bu yazıda Lisp dil ailesinin büyük ağabeyi, endüstri standardı olan Common Lisp'i kullanacağız.

Bu yazı hiç Lisp bilmeyenlere Lisp öğreten bir yazı değildir. Ayrıca karmaşık web uygulamaları ile nasıl başa çıkacağınızı da göstermemektedir. Sadece Common Lisp ve web programlamaya dair mümkün olan en basit ısınma turlarından biridir.

Kolları Sıvıyoruz

Lisp ile web uygulamaları geliştirmek için Franz Inc. tarafından geliştirilen AllegroServe web sunucusunu kullanacağız. AllegroServe, tamamen Common Lisp ile yazılmış olup, LLGPL ile dağıtılmaktadır. Esas olarak Franz'ın kendi Lisp ortamı olan AllegroCL üzerinde çalışması için tasarlanan AllegroServe'ü diğer Lisp ortamlarında çalıştırabilmek için bir ara katman kullanılıyor. Adı Portable AllegroServe olan bu katman programı, içinde AllegroServe programını da barındırıyor ve beraber dağıtılıyor. Debian GNU/Linux paket deposunda bulunan cl-aserve isimli paket de aslında Portable AllegroServe'ü içeriyor.

Programlarımızı üzerinde geliştireceğimiz ortam olarak SBCL'yi (Steel Bank Common Lisp) seçtik. Tabii ki olmazsa olmaz editörümüz de Emacs. Eğer sisteminizde, bu yazıda kullanacağımız, Emacs, SBCL ve SLIME (bir hayli gelişmiş bir Emacs Lisp geliştirme ortamı) yüklü değilse, Emre Sevinç'in " Common Lisp Geliştirme Ortamı Kurulumu" başlıklı makalesine gözatmanızı öneriyoruz. Ayrıca, eğer Lisp'e eğlenceli bir başlangıç yapmak isterseniz Conrad Barski tarafından yazılmış ve Seda Çelebican tarafından Türk diline çevrilmiş " Lisp ile TILSIMLI ve Renkli Programlama: Lisperati" başlıklı yazıyı okumanızı öneririz.

Artık kuruluma başlayabiliriz. Web uygulamalarızı geliştirebilmemiz için gerekli olan web sunucumuzu, aşağıdaki komut ile sistemimize kuruyoruz.

# apt-get install cl-aserve

Şimdi, IDE-ötesi programımız Emacs'ı açıp, M-x slime komutunu verip Emacs ile sbcl arasında iletişim kuracak ve Lisp kodlarımızı yazarken bize pek çok kolaylık sağlayacak olan SLIME'ı çalıştırıyoruz. Komut satırından

$ emacs

ve emacs açıldıktan sonra, emacs penceresinde

M-x slime

komutları yeterli olacaktır. (Burada M-x, Meta tuşunu basılı tutup, x tuşuna basmayı ifade ediyor. Meta tuşu, eğer IBM uyumlu PC klavyesi kullanıyorsanız genellikle üzerinde Alt yazan tuştur.)

SLIME'ı ilk kez çalıştırıyorsanız bir süre gerekli SLIME kodları derlenecek (sadece bir kereliğe mahsus) ve sonra da karşınıza Lisp'in ünlü Read-Eval-Print Loop'u (REPL yani Oku-Değerlendir-Bas Döngüsü) çıkacaktır.

CL-USER>

Şimdi sistemimize AllegroServe'ü yüklemek için aşağıdaki komutu çalıştırmamız gerekiyor:

CL-USER> (require :aserve)
("URI" "ACL-SOCKET" "ASERVE")


Burada yazdığımız komut, ilk satırdaki (require :aserve) kısmı. Baştaki CL-USER bize şu anda hangi paketin içerisinde olduğumuzu gösteriyor. > karakteri de Lisp'in bizden bir şeyler girmemizi istediğini söylüyor. İkinci satır ise, Lisp'in çalıştırdığımız komuta verdiği yanıt. Bunu çalıştırdığımız ifadenin geridönüş değeri olarak da düşünebiliriz. Artık AllegroServe programının bize sağladığı fonksiyonları kullanabiliriz. Bir sonraki bölüme geçmeden önce Lisp'teki paket kavramından bahsetmek ve bunun bir tür "namespace" tanımlamak için kullanılan kavramlardan biri olduğunu belirtmek faydalı olacaktır. Lisp'teki paket kavramını yazılım karmaşıklığı ile başa çıkmak, işlevselliği yalıtmak, vs. gibi işler için C#'taki "namespace" ya da Java'daki "package" mekanizması gibi düşünebilirsiniz. Daha detaylı bilgi için PCL'nin ilgili bölümüne ya da The Complete Idiot's Guide to Common Lisp Packages belgesine bakabilirsiniz.

Biraz Aserve

İlk fonksiyonumuz start fonksiyonu. Bu fonksiyon http sunucumuzu başlatmamıza yarıyor. Bu fonksiyonu aşağıdaki gibi çağırıyoruz:

CL-USER> (net.aserve:start :port 3000)
#<NET.ASERVE:WSERVER port 3000 {A3BD9C9}>


Fonksiyonumuz net.aserve isimli pakete ait olduğu ve şu anda biz CL-USER paketinin içinde çalıştığımız için, paket ismini fonksiyon isminin önüne yazmamız ve aralarına : işareti koymamız gerekiyor. Bu uzun yazış şeklinden kaçınmak için use-package fonksiyonunu kullanmamız yeterli. (Aman dikkat, eğer şu anda içinde çalışılan paketle use-package ile ihraç (export) edilen paketler aynı isimli fonksiyon, makro veya değişken kullanıyorlarsa, "sembol çakışması hatası" gerçekleşir. Bu yüzden use-package dikkatli kullanılmalıdır. Burada use-package kullanmamızın nedeni, uzun fonksiyon isimlerinden kaçınmak istememizdir. Common Lisp'teki paket yönetimi ile ilgili olarak Practical Common Lisp'in ilgili bölümüne göz atabilirsiniz.)

CL-USER> (use-package :net.aserve)
T


Artık net.aserve paketindeki -yani AllegroServe'ün- fonksiyonlarına, makrolarına ve global değişkenlerine, isimlerinin önünde paket ismi olmadan erişebileceğiz.

Web sunucumuzu başlatmamıza yarayan start fonksiyonu, hepsi "keyword" parametresi olmak üzere tam 19 parametre alıyor (Common Lisp bir fonksiyona parametre geçirmek için muazzam bir esneklik sunar, detaylar için lütfen Practical Common Lisp'in Functions bölümüne bakınız). Ancak bu parametrelerin çoğu için şimdilik varsayılan değerleri değiştirmemiz gerekli değil. Biz sadece :port parametresinin değerini 3000 olarak belirledik ve web sunucumuzun 3000 numaralı porttan yayın yapacağını söyledik. 3000 sayısının pek özel bir anlamı yok, standart 80 numaralı http port numarasını vermememizin sebebi öncelikle bunun "root" kullanıcı yetkisini gerektirmesi (UNIX ve türevi sistemlerde 1024 numaralı portun altındaki port numaralarının "root" yetkileri gerektirmesi gibi) ve/veya aynı zamanda 80 numaralı portunuzun başka programlar tarafından kullanımda olma olasılığı (Apache web sunucusu, vb.). start fonksiyonu da bize bir WSERVER nesnesi geri gönderdi (Lisp ve nesneye yönelik programlama! :). Fazla detaya inmeden şunu söyleyelim ki, start fonksiyonu başarıyla bir WSERVER nesnesi yarattı, yani sunucumuz localhost'umuzun 3000 numaralı portunda ziyaretçilerini beklemeye koyuldu.

Eğer http://localhost:3000 adresini Firefox (ya da Emacs içinden w3m) gibi bir web tarayıcı programından açarsak, çok sevimli bir 404 sayfa bulunamadı sayfasıyla karşılaşıyoruz. Bu mesajı size Allegro web sunucusunun verdiğini sayfanın altındaki Allegro ve sürüm numarasından anlayabilirsiniz. (Eğer böyle bir şey olmaz da "connection refused" benzeri bir uyarı penceresi ile karşılaşırsanız o zaman lütfen adresi doğru yazdığınızı, sunucunun çalıştığını ve 3000 numaralı portu daha öncede başka bir programın dinlemediğini kontrol edin.) Sayfa bulunamadı şeklindeki bir web sayfası ile karşılaşmanızın sebebi web sunucusunda bırakın index'i, daha hiçbir sayfayı yayınlamıyor oluşumuz. Şimdi geleneklere saygı göstererek bir "merhaba dünya" yazısını tarayıcımızda göstermek istiyoruz. Bunun için Emacs'taki REPL ortamında yani Common Lisp komut satırında

CL-USER> (publish :path "/"
		  :content-type "text/html"
		  :function
		  #'(lambda (req ent)
		      (with-http-response (req ent)
			(with-http-body (req ent)
			  (format (request-reply-stream req) "merhaba dünya!")))))
#<COMPUTED-ENTITY {9FDFBD1}>


yazdıktan sonra ve şimdi http://localhost:3000 adresine gittiğimizde bizi

Merhaba Dünya

yazısı karşılıyor. (Ne büyük sürpriz!)

AllegroServe'ü diğer web sunucularından (mesela Apache) ayıran en önemli özelliklerden biri, sadece dosyaları ve dizinleri değil doğrudan bir Lisp fonksiyonunu yayımlayabilmesi. Bu özellik sayesinde sunucumuza "eğer kullanıcı şu adrese giderse bu fonksiyonu çalıştır" diyebiliyoruz. Lisp'de fonksiyonlar birinci sınıf nesne olduklarından [1], bir adrese gidildiğinde çağrılacak fonksiyonu örnekte gösterildiği gibi dinamik olarak da yaratabiliriz.

Fonksiyonlarımızla, URL'leri eşleştirmemize yarayan publish fonksiyonuna verdiğimiz parametrelere kısaca bir gözatalım.
  • :path anahtar sözcüğü kullanarak fonksiyona geçirdiğimiz değer (:path "/") fonksiyonumuzu eşleştireceğimiz adresi belirtiyor. Yukarıdaki örnekte "/", yani kök adresi belirttik.
  • :content-type çalıştıracak fonksiyonun üreteceği MIME tipini belirtiyor ki bu da genellikle "text/html"dir.
  • :function Çalıştırmak istediğimiz fonksiyon.

Bir adresle eşleştirdiğimiz fonksiyon iki parametre alan bir fonksiyon olmalı. Birincisi http istek (request) nesnesi, ikinci ise publish fonksiyonunun döndürdüğü "entity" nesnesi. Bunların değerleri, bir http isteği gerçekleştiğinde, AllegroServe tarafından fonksiyonumuza veriliyor.

Gelelim oluşturduğumuz fonksiyonun içinde bulunan with-http-response ve with-http-body sembollerine. Bu semboller AllegroServe'de tanımlanmış iki makroyu ifade ediyor. with-http-response makrosu sayesinde, kullanıcıya bir yanıt (response) hazırladığımızı belirtiyoruz. with-http-response, gerekli olan HTTP başlık (header) değerlerini bize iş bırakmadan hazır hale getiriyor. Diğer sevimli makromuz with-http-body ile artık HTTP cevabımızın içeriğini olşuturmaya hazır olduğumuzu belirtiyoruz. İki makroya da fonksiyonumuza gelen parametreleri vermek zorundayız.

Fonksiyonumuzda ekrana "merhaba dünya" yazısını yazdıran kısım, format fonksiyonunu çağırdığımız kısım. format fonksiyonunu C'deki sprintf fonksiyonuna benzetebiliriz (ancak burada belirtmeliyiz ki format çok güçlü bir fonksiyon olup, barındırdığı kurallardan ötürü bir mini dil (DSL, Domain Specific Language) olarak görülmelidir, tıpkı LOOP gibi. Lisp'teki FORMAT ile ne kadar acayip şeyler yapılabileceğini merak edenler Practical Common Lisp'in ilgili bölümüne bakabilirler). Kısaca bir "stream"e, belirli bir şablona göre oluşturduğu bir karakter katarını gönderir. Örneğimizde format'a akım olarak kullanıcıya yanıt vermek için kullandığımız "stream" nesnesini veriyoruz. Bu nesneye ulaşmak için de request-reply-stream fonksiyonunu kullandık.

AllegroServe'ün Apache'nin yaptığı gibi dosyaları ya da dizinleri yayınlamasını istersek, kullanmamız gereken fonksiyonlar publish-file ve publish-directory fonksiyonları.

CL-USER> (publish-file :path "/makale.txt"
		       :file "/home/hb/lisp-web.01.txt")
#<NET.ASERVE::FILE-ENTITY {98CB3A1}>



Artık http://localhost:3000/makale.txt adresinden ev dizinimdeki lisp-web.01.txt dosyasına ulaşabilir durumdayım.

Şimdi biraz daha gelişmiş bir "merhaba dünya" uygulamasıyla, HTTP isteğinden gelen GET ve POST değişkenlerine nasıl ulaşacağımıza bakalım. Henüz Lisp ile HTML üretmeyi bilmediğimiz için form sayfasını durağan (statik) bir HTML sayfası olarak bir dosyaya kaydediyor ve bu dosyayı yayınlıyoruz.

Form sayfamız:

<html>
<head>
  <title>Form sayfasi</title>

</head>

<body>
<form action="/handle-form" method="post">
   <span>Gezegen:</span>
   <input type="text" name="gezegen" size="40"/>
   <input type="submit" value="Yolla gitsin"/>

</form>
</body>



CL-USER> (publish-file :path "/form"
		       :file "/home/hb/form.html")
#<NET.ASERVE::FILE-ENTITY {9068C49}>


Şimdi sıra bu formu yorumlayacak olan fonksiyonumuzu hazırlamakta.

CL-USER> (defun handle-form (req ent)
	   (with-http-response (req ent)
	     (with-http-body (req ent)
	       (let ((gezegen (request-query-value "gezegen" req))
		     (req-stream (request-reply-stream req)))
		 (if (not (equal gezegen ""))
		     (format req-stream "Selam ~A!" gezegen)
		     (format req-stream "Ne bileyim nerden geldin!"))))))
HANDLE-FORM


CL-USER> (publish :path "/handle-form" :content-type "text/html" :function 'handle-form) #<COMPUTED-ENTITY {A018F91}>
Bu kez, ilk örnekteki gibi fonksiyonumuzu lambda kullanarak isimsiz değil, defun kullanarak isim vererek yarattık. Daha sonra publish fonksiyonuyla yarattığımız fonksiyonu olması gereken adreste yayına verdik. Bu örnekte, formdan gelen bilgiyi almak için request-query-value fonksiyonunu kullandık. Bu fonksiyonun ilk parametresi formdan gelen değişkenin adı, ikincisi ise request nesnesi.

Gelin HTML üretelim

Bu bölümde Lisp ile HTML üretmek için kullanacağımız pek ufak bir kütüphane oluşturacağız [2]. HTML dili de Lisp gibi "prefix" gösterime ve iç içe geçen yapılardan oluşan yapıya sahip olduğundan bu pek zor bir iş olmayacak. İç içe geçen HTML etiketlerini iç içe geçmiş Lisp fonksiyonları olarak düşünmek gayet doğaldır.

İlk önce üreteceğimiz HTML çıktısının gideceği bir "stream" değişkenini oluşturmalıyız. Gerektiğinde bu değişkenin değerini değiştirerek oluşturduğumuz HTML çıktısını standart çıktıya ya da bir dosyaya yazabiliriz.


CL-USER> (defvar *html-stream* nil)
*HTML-STREAM*
Şimdi oluşturacağımız çıktıları belirli bir HTML etiketi arasına yerleştirecek olan % makrosunu yazıyoruz. % ismi tamamen bizim seçimimiz. Alternatif olabilecek with-html-tag gibi uzun bir isim yerine, çok kullanılacağını düşündüğümüz için % ismini kullandık.

CL-USER> (defmacro % (tag (&rest attrs) &body body)
	   `(progn 
	     (format *html-stream* "<~(~A~)~A>" ',tag (process-attrs (list ,@attrs)))
	     ,@body
	     (format *html-stream* "</~(~A~)>" ',tag)))
%


% makrosuna bir gözatalım. İlk satırda defmacro ile % isminde bir makro tanımlamakta olduğumuzu belirtiyoruz. % makrosu, parametre olarak; HTML etiketinin adını, özniteliklerini ve etiketin başlangıç ve bitişi arasında çalıştıracağı bir Lisp kod bloğunu alıyor. İlk format fonksiyonunu çağırdığımızda, < ve > karakterlerinin arasına etiketin adını ve özniteliklerini yazıyoruz. % makrosuna öznitelikleri (özniteliğin-adı değeri adı değeri ... ) gibi bir liste halinde veriyoruz. Aşağıda tanımlayacağımız process-attrs fonksiyonu bu listeyi HTML sözdizimine uygun bir şekilde işleyip geri döndürüyor. Daha sonra aldığımız Lisp kodunu çalıştırıp, kapanış etiketini oluşturuyoruz.

Aşağıda, process-attrs fonksiyonunu nasıl tanımladığımızı görebilirsiniz.

CL-USER> (defun process-attrs (attrs)
	   (if (null (cdr attrs))
	       ""
	       (if (not (null (second attrs)))
		   (format nil " ~(~A~)="~A"~A" (first attrs) (second attrs)
			   (process-attrs (cddr attrs)))
		   (process-attrs (cddr attrs)))))
PROCESS-ATTRS

% makrosunun örnek bir kullanımı aşağıdaki kodda gösteriliyor. Öncelikle let yapısını kullanarak, *html-stream* değişkeninin değerini *standard-output* yapıyoruz. Bunu yapmamızın nedeni, oluşacak HTML kodunu ekranda görebilmek. % makrosunu p sembolü ve (:align "left") listesiyle çağırdık ve içinde *html-stream*'e "hello world" karakter katarını bastık. Tam da istediğimiz gibi HTML kodumuz ekranda görüntülendi. Oluşan HTML kodunun altındaki satırda bulunan NIL, çalıştırdığımız kodun geri dönüş değeri. Bu değer *html-stream* akımına basılmıyor.

CL-USER> (let ((*html-stream* *standard-output*))
	   (% p (:align "left")
	     (format *html-stream* "Merhaba dünya!")))
<p align="left">Merhaba dünya!</p>
NIL

İkinci makromuz %=, aynı % makrosu gibi çalışacak, ancak bir Lisp bloğu alıp çalıştırmak yerine, bir Lisp nesnesi alıp onu ekrana basacak. Yeni makromuzun tanımı aşağıda.

CL-USER> (defmacro %= (tag (&rest attrs) obj)
	   `(progn 
	     (format *html-stream* "<~(~A~)~A>" ',tag (process-attrs (list ,@attrs)))
	     (princ ,obj *html-stream*)
	     (format *html-stream* "</~(~A~)>" ',tag)))
%=


CL-USER> (let ((*html-stream* *standard-output*))
	   (%= a (:href "http://www.google.com") "Google'a git"))
<a href="http://www.google.com">Google'a git</a>

NIL

Uzun let yapısından ve format çağrılarından kurtulmak için birkaç ufak yardımcı fonksiyon tanımlayalım.

CL-USER> (defun pr (obj)
	   (princ obj *html-stream*))
PR

CL-USER> (defmacro prf (format &rest args)
	   `(format *html-stream* ,format ,@args))
PRF

pr ve prf, sırası ile princ ve format fonksiyonlarının stream değişkenlerini kendiliğinden *html-stream*'ye bağlıyor. Özellikle prf makrosu bize büyük bir tasarruf sağlayacak.

Bir diğer makromuz aşağıda tanımladığımız with-html-stream makrosu. Parametre olarak bir akım nesnesi ve Lisp kod bloğu alan bu makro, *html-stream*'i aldığı "stream" nesnesine bağlıyor ve bloğu çalıştırıyor.


CL-USER> (defmacro with-html-stream ((&optional (stream *standard-output*)) &body body)
	   `(let ((*html-stream* ,stream))
	     ,@body))
WITH-HTML-STREAM

with-html-stream makrosunu kullanarak, oluşturduğu HTML kodlarını string olarak yollayan bir makro yazıyoruz:

CL-USER> (defmacro html-to-string (&body body)
	   (let ((g-stream (gensym)))
	     `(with-output-to-string (,g-stream)
	       (with-html-stream (,g-stream)
		 ,@body))))
HTML-TO-STRING

CL-USER> (html-to-string
	   (% div (:align "center" :id "mainPane")
	     (%= span (:class "normalText")
		 "Naber dünya?")))
"<div align="center" id="mainPane"><span class="normalText">Naber dünya?</span></div>"




Oluşturduğumuz bu araçlarla, biraz daha üst seviye olan page makrosunu oluşturabiliriz.

CL-USER> (defmacro page ((&key (title "Untitled")) &body body)
	   `(% html ()
	     (% head ()
	       (%= title () ,title))
	     (% body ()
	       ,@body)))
PAGE

Page makrosu, title parametresini alarak bir HTML belgesini oluşturuyor. Burada Lisp makrolarının çok doğal biçimde şablon görevini üstlendiklerini görebiliyoruz.

CL-USER> (publish :path "/merhaba2"
		  :content-type "text/html"
		  :function
		  #'(lambda (req ent)
		      (with-http-response (req ent)
			(with-http-body (req ent)
			  (with-html-stream ((request-reply-stream req))
			    (page (:title "Merhaba Dünya!")
			      (pr "ikinci kez merhaba!")))))))
#<COMPUTED-ENTITY {A185461}>

Yukarıdaki örnekte sayfamızı oluşturan kod sadece 2 satır, ama daha öncesinde çok fazla satır yazmak zorunda kalıyoruz. Her tembel programcı gibi bunları yazmamanın bir yolunu aradığımızda imdadımıza yine Lisp makroları yetişiyor. Aşağıda tanımladığımız webfn makrosu, with-* makrolarını yazmaktan kurtulmamızı sağlıyor.

CL-USER> (defmacro webfn (&body body)
	   `#'(lambda (req ent)
		(with-http-response (req ent)
		  (with-http-body (req ent)
		    (with-html-stream ((request-reply-stream req))
			,@body)))))
WEBFN

CL-USER> (publish :path "/merhaba3"
		  :content-type "text/html"
		  :function
		  (webfn
		    (page (:title "Merhaba")
		      (% div (:align "center")
			(%= p () 
			    "Birinci paragraf")
			(%= p ()
			    "ikinci paragraf")))))
#<COMPUTED-ENTITY {91DCC41}>


Gördüğünüz gibi makrolar sayesinde yazdığımız kod epey azaldı. Eğer "şu publish kısımlarını da yazmasak" derseniz, aşağıdaki makroya bir gözatmanızı tavsiye ederiz.

CL-USER> (defmacro defwebfn (name &body body)
	   `(publish :path ,(format nil "/~(~A~)" name)
	             :content-type "text/html"
	             :function (webfn ,@body)))
DEFWEBFN


CL-USER> (defwebfn welcome-page
           (page (:title "Hosgeldiniz")
	     (% div (:align "center")
	       (prf "Hosgeldiniz!"))))
#<COMPUTED-ENTITY {A3B99A1}>


defwebfn makrosu webfn ile bir fonksiyon yaratıp kendiliğinden yayınladı. Şimdi http://localhost:3000/welcome-page adresine gidip sonucu görebilirsiniz.

Veritabansız Olur Mu? Olmaz!

Common Lisp ile MySQL bağlantısı yapabilmek için Debian GNU/Linux paket deposundaki cl-sql-mysql paketine ihtiyacımız var. Aşağıdaki komutu çalıştırarak sistemimize bu paketi yüklüyoruz.

# apt-get install cl-sql-mysql

cl-sql-mysql paketi, cl-sql ve cl-sql-uffi paketlerine bağımlı olduğu için apt-get bize "bu paketleri de yükleyeyim mi?" diye soracaktır. İşi ustasına bırakıp yüklemesine izin veriyor ve Lisp kodu içinden MySQL ile konuşmamızı sağlayacak kütüphaneyi kuruyoruz.

Şimdi Lisp geliştirme ortamına geri dönüp, yeni yüklediğimiz bu paketleri Lisp'e dahil ediyoruz:

CL-USER> (require :clsql-mysql)
NIL

cl-sql, veritabanı ile nesneler arasında birebir ilişki kurabilen ve SQL sorguları yerine daha üst seviyede çalışabilmeyi sağlayan bir kütüphanedir. Fakat bu özelliklerin nasıl kullanılacağının anlatılması ancak başka bir yazının konusu olabilir. Bu yazıda sadece basit SQL sorgularının Common Lisp ile yapılmasını ve sorguların sonuçlarının web uygulamasında kullanılmasını anlatmakla yetineceğiz.

Örneğimizde hayali bir kitapçı dükkanının hayali kitaplarını tutacağı bir tabloluk basit bir veritabanını kullanacağız. Veritabanını, biricik tablosunu ve birkaç kitap kaydını yaratmak için aşağıdaki SQL sorgusunu kullanabiliriz. Bu aşamada MySQL ile bir miktar programlama deneyimine sahip olduğunuzu var sayıyoruz:


CREATE DATABASE bookstore;

CREATE TABLE books (
      id INT NOT NULL AUTO_INCREMENT,
      title VARCHAR(120) NOT NULL,
      author VARCHAR(60) NOT NULL,
      PRIMARY KEY (id), UNIQUE(id));

INSERT INTO books (title, author)
      VALUES ('I'm sure you're joking Mr. Feynman', 'Richard Feynman');

INSERT INTO books (title, author)
      VALUES ('Also Sprach Zaradustra', 'Freidrich Nietzsche');

INSERT INTO books (title, author)
      VALUES ('The Relativity Theory', 'Albert Einstein');

INSERT INTO books (title, author)
      VALUES ('ANSI Common Lisp', 'Paul Graham');

Veritabanına bağlanmak için clsql'nin connect fonksiyonun kullanmamız gerek. clsql:connect fonksiyonunu aşağıdaki gibi çağırıyoruz.


CL-USER> (clsql:connect '("localhost" "bookstore" "root" "parola"))
#<CLSQL-MYSQL:MYSQL-DATABASE localhost/bookstore/root OPEN {A630C41}>
connect fonksiyonuna parametre olarak sırasıyla veritabanının bulunduğu sunucunun alan adını, kullanacağımız veritabanının adını, veritabanı kullanıcısının adını ve son olrak kullanıcının parolasını içeren bir liste vermemiz gerekiyor. Eğer veritabanına bağlanmayı başaramazsak, connect fonksiyonu bir hata yaratıyor. (Hata yakalama fonksiyonları hakkında bkz. Practical Common Lisp)

Veritabanımızdan veri çekmek için clsql:query fonksiyonunu kullanacağız. clsql:query fonksiyonu, bir SQL sorgusu alıp, eşleşen kayıtları ve tablodaki alan adlarını döndürüyor. (Common Lisp'de bir fonksiyon birden fazla değer döndürebilir, bu çok elemanlı dizi döndürmekten biraz farklı bir durumdur. Detaylı bilgi için bkz.: http://www.lispworks.com/documentation/HyperSpec/Body/m_multip.htm ve http://www.gigamonkeys.com/book/the-special-operators.html) Aşağıda query fonksiyonunun örnek kullanımını görebilirsiniz.

CL-USER> (clsql:query "SELECT title AS 'baslik' FROM books")

(("I'm sure you're joking Mr. Feynman") ("Also Sprach Zaradustra")
 ("The Relativity Theory") ("ANSI Common Lisp"))
("baslik")

Şimdi, tablomuzdan kitap listesini çekip, bunu bir HTML tablosu halinde web sunucumuzda yayınlayabiliriz:

CL-USER> (defwebfn book-list
          (% page (:title "Book List")
            (multiple-value-bind (rows labels) (clsql:query "SELECT * FROM books")
              (% table (:border "1")
                (% tr ()
                  (dolist (label labels)
                    (%= th () label)))
                (dolist (row rows)
                  (% tr ()
                    (dolist (field row)
                      (%= td () field))))))))
#<COMPUTED-ENTITY {A6DA9D1}>
Denemek için web tarayıcınız ile http://localhost:3000/book-list adresini ziyaret edebilirsiniz.

Bitirirken

Giriş seviyesindeki bu yazıda Common Lisp ile Allegro Portable Web Server kullanarak ve Lisp'in makro geliştirme gücünden faydalanarak Internet programlamanın nasıl yapılabileceğini çok basitçe gördük. Bu aşamada arrtık en temel şekilde Lisp kullanarak HTML üretme ve MySQL gibi popüler bir veritabanına bağlanma konusunda belli bir izlenime kavuştuğunuzu ümit ediyoru.

Common Lisp gibi büyük ve köklü geçmişe sahip bir dil ve bu dil ile Internet programlama gibi detaylı bir konuya dair bu satırlara sığdıramadığımız pek çok şey mevcuttur.

Anlattıklarımız bu tür işleri yapmanın tek yolu değil elbet, alternatif yöntemler, çok daha gelişmiş UnCommon Web gibi "framework"ler Internet'te sizin tarafınızdan keşfedilmeyi bekliyor.

UnCommon Web ile ilgili çok güzel bir demonun vidosunu izlemek isterseniz lütfen aşağıdaki adreslerden faydalanmaktan çekinmeyin:

http://common-lisp.net/movies/hello-world.torrent

http://common-lisp.net/movies/hello-world.mov


Haldun Bayhantopçu
30 Temmuz 2005 - İstanbul

Notlar

[1] Birinci sınıf nesneden şunu kastediyoruz: Lisp'de isimsiz bir fonksiyon yaratıp, bunu başka bir fonksiyona parametre olarak verebilir, o fonksiyon da aldığı fonksiyonu başka bir fonksiyonla olan bileşkesini oluşturur ve bir liste üzerindeki tüm değerlere bu oluşturduğu fonksiyonu uygular, ve sonuçlarını bir listede toplar ve başka bir fonksiyona geri verebilir. Kısacası, Lisp'de bir fonksiyonu şekere bile katabilirsiniz.

[2] Bu kütüphanenin temel makrosu Paul Graham'ın ANSI Common Lisp kitabından alınmıştır.

Yazının özgün ve kalıcı adresi:http://ileriseviye.org/arasayfa.php?inode=cl-web.html

Görüşler

0
darkhunter
Emeği geçen herkese sonsuz teşekkürler, kısaca bir göz attım, benim bile soru sormamı engelleyecek kadar ayrıntılı ve basit olmuş :-) Uykumu alır almaz 3. kuşak web programcısı olmak için çalışmalarıma başlayacağım ;-)
0
FZ

First there was GOTO, and all was ... bearable ...
GOTO begot the subroutine, and all was ... better ...
The subroutine begot the function, and all was good.

First there the CGI, and all was ... bearable ...
CGI begot the servlet and jsp, and all was ... better ...
Then there was UCW, and all was good.


http://common-lisp.net/project/ucw/ adresinden...
0
sundance
Yıllar sonra "Bir zamanlar Fazlamesai'de bir Lisp harekatı vardı, birkaç cengaver 'bırak onu ya, o akademisyenlerin mastürbasyon dili' ya da '70'lerde kalmış dil, C++ yokmuş o zamanlar' diyenleri utandırarak, eski ile klasiğin arasındaki farkı göstermişti" diye hatırlanacaksınız :)

Elinize sağlık.
0
FZ
Daha yapılacak çok iş var, bu makale bitmeden ikinci, üçüncü, ... makalelerin konusunu düşünmeye başladık bile ;-)

Sanırım sırada Pascal'ın Lisp kılavuzunun çevirisi var. Kendisinden bu hafta içinde haber bekliyoruz, son rötuşlarını atar atmaz devreye girip gerekeni yapacağız.

Ardından belki UnCommon Web'in yazarı Marco Baringer'in mükemmel SLIME videosundan yola çıkıp Lisp ortamında debugging, tracing, disassembly (ve daha pek çok mevzu) nasıl yapılır bunu anlatan bir yazı hazırlamak düşünülebilir. Videolar çok güzel, onlara eşlik eden ve adım adım anlatan bir yazı da yazılsa güzel olur.

Sahi, Paul Graham çeviri harekatı vardı, ne oldu? Bekliyoruz yeni makale çevirilerini ;-)
0
robertosmix
İnternet programlama??!?!
0
FZ
Haydi şimdi terimler üzerinden abuk sabuk bir flamewar'a girelim, birileri bunun nesi Internet programlama desin, bir başkası neresi değil desin, beriki iki kelime yazım !??!??!? filan eklesin, peh! desin öteki, ardından bir kısmımız kolaysa Lisp ile TCP/IP stack yazın, Apache yazın hahahahaha filan desin. Bunlar olurken bir başkası dalıp yok olm biz burada J2EE vb. türü şeylerle çok acayip e-ticaret sitesi, banka sitesi yapıyoruz desin, ona kızan biri, boşversene yahu PHP "scriptleri" ile işimi görüyorum ben babalar gibi ne uğraşacam Java zor, Lisp daha zor, bana ne bana ne zor işte desin.

Tabii hep teknik ve ciddi muhabbet edecek değiliz ya, arada eğlenmek de lazım :)
0
bm
kolaysa Lisp ile TCP/IP stack yazın

Pardon, bu yapilmadi mi zannediyorsunuz? Chaosnet'ten IP'ye kadar herseye beglanabilen Lisp makinelerini bir tarafa birakin, bunu hobi olarak yapan da var: http://www.cliki.net/Slitch


0
FZ
Doğrudan Internet programlamayla ilgisi olmamakla beraber bir başka yazıyı paylaşalım. Ana tema ürün konfigürasyon yönetimi, revizyon kontrol sistemi ve yazılım ürünlerinin konfigürasyon, birleştirilme ve dağıtılma meselesi:

http://home.comcast.net/~prunesquallor/changesafe.html

Özellikle yazılım konfigürasyonları ile uğraşanların ilgisini çekebilecek bu yazının sonundaki şu paragraf bir hayli anlamlı:


Why Lisp

The ChangeSafe PCM product did not need to be written in Lisp. Other change set based PCM systems exist that have been written in C, Java, Haskell, etc. But none, to my knowledge, build the PCM system on top of a change-managed language substrate. The ChangeSafe core engine can be adapted to model any object that changes, not just files.

There are major obstacles to adding persistent versioning to other languages. Meta-object protocols are still experimental in most languages, while Lisp's MOP is stable, well-supported and mature. The abstraction of complex control flow made possible through the use of continuation-passing-style is extremely cumbersome in most languages, if it is possible at all. Few languages with explicit types can deal with anonymous procedures when used in this manner. While the example above did not illustrate it, macros are used to create an intuitive, streamlined syntax for common operations. Algebraic notation is hostile to macros.

I don't believe any other computer language could be as easily adapted to versioned object modelling.
0
bm
Genelde film reklami yapmayi pek sevmiyorum ama bu sefer Vancouver Lisp gurubunun bir toplantisindaki sunumdan Bill Clementson'un hazirladigi bir filmi seyretmeden haber veriyorum. (Clementson genelde duzgun is yapiyor cunku). Ozellikle toplantidaki sunum tarzi hosuma gitti. Konu bu sefer UCW gibi 'continuation' kullanan Smalltalk'la yazilmis Seaside. Detaylar burada:

http://bc.tech.coop/blog/050826.html


0
noem
benim daha önceden email ile yazıp cs@cs.bilgi.edu.tr ile danıştığım çalışma stilini sorduğum basit 3 fonksiyondan ikisini makroya çevirip buraya koymuşlar. oldukçada geliştirmişler faydalı olduğum içinde oldukça mutlu olduğumu bildirmek isterim. emaillerin metinlerini aşağıya kopyalıyorum şimdi
bu kodları daha ilk lispe merak sardığım zaman yazmıştım garibinize gidecek bazı hatalar da o yüzden olmuştu :

lisp ile ilgili sayfalarda web tasarımında nasıl kullanılabileceğine dair birçok konu okudum (asıl esin kaynağım lisp ile web programlamaya dalış). ancak diğerlerinin açık kodlarını okurken biraz bana garip geldi. bende 3 basit komut yazdım. ilk iki komut sadece web sayfalarında değil xml oluşturmada da kullanılabilir diye düşünüyorum. tagların ve parametre adlarının çift tırnak içinde yazılması garip gelebilir. ben de ilk etapta onları çift tırnaksız veya sembolleştirmeden yazayım diye düşündüm. sonra farkettim ki bunların veri olarak girilmesi hem lispin mantığına uygun, hemde bu tagların ve parametre adlarının istenildiğinde kodu değiştirmeden değiştirilebilmesinin sağladığı kolaylıktan dolayı bu şekilde bıraktım. aşağıdaki gibi bir web sayfasının bir kalıp olarak oluşturulup üzerinde kalıp bozulmadan istenilen değişikliğin yapılabileceğini düşünüyorum.

şu an en büyük sorunum bu kodları yazarken "\\"" karşılığının " olması gerekirken \\" olmasıdır. maalesef birçok common lisp ortamında denedim olmadı. buna ihtiyacımın sebebi xml formatında tag parametresinin isim="içerik" yapısında olması zorunluluğu. bununla ilgili sorunuma cevap verirseniz sevinirim.

;içeriği olan taglar için: html body title body p gibi
(defun % (tag parametre &rest icerik) (format nil "~{~a~}~%" tag parametre icerik tag) )

;içeriği olmayan taglar için: input img gibi
(defun %- (tag parametre) (format nil "~%" tag parametre) )


;basit css tanımlaması
(defun %% (tag parametre) (format nil "~a {~%~a:~a;~%~}~%" tag parametre) )



;örnek bir web sayfası
; genel kalıp (% tag '(parametreler) içerik)
; tag parametreler ve içerik değişkenle veri aktarmadığı sürece çift tırnak içinde
; yazılmalıdır. parametreler ikişerli olarak düşünülmelidir. birinci parametre
; parametre adı ikinci parametre parametre içeriği üçüncü parametre 2.parametrenin
; adı dördüncü parametre 2. parametrenin içeriği ve bu şekilde devam eder.
; içerik tek parça olmak zorunda değildir. o yüzden rest kullandım bu da body gibi
; tagların birçok iç tagına sahip olmasını sağlıyor. text/html çıktısının şu anki en
; büyük sorunu örneğin body tagında geçen bgcolor="blue" olması gerekirken
; bgcolor=\\"blue\\" olması bunu istersem hiç çift tırnak koymayarak ta halledebilirim
; ama öyle kalmasını tercih ettim.

(% "html" '()
(% "head" '()
(% "title" '() "Deneme sayfası" )
(%- "meta" '( "http-equiv" "content-type"
"content" "text/html; charset=windows-1254"))
)
(% "body" '("bgcolor" "blue" "alink" "black")
(% "h1" '() "Bu sayfanın konusu yok")
(% "p" '("align" "left")
"bu deneme sayfasının ilk satırı")
(% "p" '()
"bu deneme sayfasının ikinci satırı")
)
)



ONLARDAN GELEN CEVAP ;


From: "Volkan YAZICI"
Sent: Sunday, August 17, 2008 9:18 PM
To:
Subject: [cs-lisp] Re: lisp ile web sayfası çalışmalarımdan

> On Sun, 17 Aug 2008, writes:
>> lisp ile ilgili sayfalarda web tasarımında nasıl kullanılabileceğine
>> dair birçok konu okudum (asıl esin kaynağım lisp ile web programlamaya
>> dalış). ancak diğerlerinin açık kodlarını okurken biraz bana garip
>> geldi. bende 3 basit komut yazdım. ilk iki komut sadece web
>> sayfalarında değil xml oluşturmada da kullanılabilir diye düşünüyorum.
>> tagların ve parametre adlarının çift tırnak içinde yazılması garip
>> gelebilir. ben de ilk etapta onları çift tırnaksız veya
>> sembolleştirmeden yazayım diye düşündüm.
>
> Aslında XML'in büyük-küçük harf duyarlı (case-sensitive) olduğu
> düşünülecek olursa, garip değil. (Ki böyle kullanılmasının taraftarı
> olmadığımı belirtmek isterim.)
>
>> sonra farkettim ki bunların veri olarak girilmesi hem lispin mantığına
>> uygun, hemde bu tagların ve parametre adlarının istenildiğinde kodu
>> değiştirmeden değiştirilebilmesinin sağladığı kolaylıktan dolayı bu
>> şekilde bıraktım.
>
> Burada sizi doğru anlamış mıyım, bakalım: "Sadece `%', `%-' ve `%%'
> fonksiyonlarını değiştirerek, kodun geri kalanında bir değişikliğe gerek
> kalmaksızın oluşacak çıktı üzerinde söz sahibi olabilirim."
> diyorsunuz. Bu doğru; fakat makro kullanan sexp-to-html yöntemleri için
> de kısmen doğru. Şöyle ki:
>
> 1. Makro kullanan sexp-to-html metodları `compile time' esnasında tüm
> sabit (statik) veriyi tek sefere mahsus olmak üzere tek bir katar
> (string) halinde üretip, `evaluation time' esnasında gerçekleşecek
> olan oldukça fazlaca bir `string concatenation' işleminden tasarruf
> etmenizi sağlıyorlar. (Bkz. CL-WHO) Bu da oldukça fazla bir başarım
> artışı olarak programınıza yansıyor. (Makroda gerçekleşen bir
> değişikliğin tüm çıktıya yansıması için, makroyu çağıran tüm
> ifadelerin tekrardan derlenmesi gerekiyor bu sebeple.)
>
> 2. Makro yerine işlemi, sizin gibi, çalışma esnasında gerçekleştiren
> uygulamalarda (Bkz. UCW), çalışma esnasında ifadelerin değerleri
> bilindiği için -- dikkat, bu makrolar için gereçerli değil -- SGML
> çıktısı üretliriken hata denetimi mümkün olmaktadır.
>
>> şu an en büyük sorunum bu kodları yazarken "\\"" karşılığının " olması
>> gerekirken \\" olmasıdır. maalesef birçok common lisp ortamında denedim
>> olmadı. buna ihtiyacımın sebebi xml formatında tag parametresinin
>> isim="içerik" yapısında olması zorunluluğu. bununla ilgili sorunuma
>> cevap verirseniz sevinirim.
>
> Bir bakalım.
>
> CL-USER> (defpackage :test (:use :cl))
> #
> CL-USER> (in-package :test)
> #
> TEST> (defun % (tag parametre &rest icerik) (format nil " ~}>~{~a~}~%" tag parametre icerik tag) )
> %
> TEST> (defun %- (tag parametre) (format nil "~%" tag
> parametre) )
> %-
> TEST> (defun %% (tag parametre) (format nil "~a {~%~a:~a;~%~}~%" tag
> parametre) )
> %%
> TEST> (% "html" '()
> (% "head" '()
> (% "title" '() "Deneme sayfası" )
> (%- "meta" '( "http-equiv" "content-type"
> "content" "text/html; charset=windows-1254")))
> (% "body" '("bgcolor" "blue" "alink" "black")
> (% "h1" '() "Bu sayfanın konusu yok")
> (% "p" '("align" "left")
> "bu deneme sayfasının ilk satırı")
> (% "p" '()
> "bu deneme sayfasının ikinci satırı")))
> "Deneme sayfası
> charset=windows-1254\\" />
>
> Bu sayfanın konusu yok
> bu deneme sayfasının ilk satırı
> bu deneme sayfasının ikinci satırı
>
>
> "
> TEST> (princ *)
> Deneme sayfası
> />
>
> Bu sayfanın konusu yok
> bu deneme sayfasının ilk satırı
> bu deneme sayfasının ikinci satırı
>
>
> "Deneme sayfası
> charset=windows-1254\\" />
>
> Bu sayfanın konusu yok
> bu deneme sayfasının ilk satırı
> bu deneme sayfasının ikinci satırı
>
>
> "
>
> Görüldüğü üzere, aslında çıtkıda bir sorun yok. Fakat siz fonksiyondan
> bir `string' döndürüdüğünüz için, çift-tırnaklar ekrana ayıklanarak
> basılıyor. Normalde bunlar mevcut değil.
>
>
> İyi çalışmalar.
>
> P.S. Bu arada ben şiddetle CL-WHO ve ParenScript seçeneğini de gözden
> geçirmenizi tavsiye ediyorum.
>
> _______________________________________________
> cs-lisp mailing list
> cs-lisp@cs.bilgi.edu.tr
> http://church.cs.bilgi.edu.tr/lcg
> http://cs.bilgi.edu.tr/mailman/listinfo/cs-lisp
>
Görüş belirtmek için giriş yapın...

İlgili Yazılar

Core-Server GNU/Linux Installer

anonim

Hemen herkesin bir miktar güçlük yaşadığı Common Lisp tabanlı web sunucu kurma problemini halledeceğini umdugum yazılımı sizlerle paylaşmak isterim.

http://www.core.gen.tr/projects/core-server-installer-latest.tar.gz*

Hata bildirilerinizi evrim _at_ core.gen.tr adresine gonderebilirsiniz.

*: Gentoo, Debian ve Ubuntu GNU/Linux dağıtımları için test edildi

eXtreme Programming ve Bir Başarı Öyküsü

FZ

Bill Clementson son blog girdilerinden birinde birkaç gün önce gerçekleştirdikleri etkinlikte sunum yapan ve bir başarı öyküsü aktaran Ken Dickey'e yer vermiş.

Avustralya kökenli başarılı bir start-up olan Memetrics'in öyküsünü çarpıcı bir dille anlatan Dickey, eXtreme Programming metodolojisi ve Common Lisp'ten faydalanarak geliştirdikleri sistemi anlatan güzel bir sunum hazırlamış.

Keskin rekabetten ve gizli silahların gücünden hoşlananlar için incelenesi bir örnek. Afiyet olsun.

Barack Obama'yı Semantik Web Üzerinden Lisp ile Sorgulamak

FZ

Öyle bir veri tabanı düşünün ki şu sorunuza doğru cevaplar veriyor: "Barack Obama nerede doğmuştu? (Nerede dünyaya gelmişti diye de yazabilirsiniz) Doğduğu yere 15 km uzaklıktaki şehirler ve kasabalar hangileri idi? 2000 yılında bu yerleşim yerlerindeki ortalama fakirlik seviyesi ne idi?"

Bunu tek bir ilişkisel veri tabanı ile kolayca yapabilir misiniz? Yahut daha doğru soru: Neden tek bir veri tabanı olsun ve bu neden ilişkisel olsun? Buyrun semantik web ve RDF kullanın. Nasıl mı?

Fonksiyonel Geometri, Lisp, Escher, Postscript: Sanat ve Bilgisayarlar

FZ

Daha önce FM'de bir Mars programlama projesi yarışması bağlamında adı geçen Frank Buss bu sefer de gündemimizi Peter Henderson'ın makalelerinden uyarladığı ve Common Lisp kullanarak gerçekleştirdiği bir fonksiyonel geometri uygulaması ile meşgul ediyor. Fonksiyonel programlamanın grafik uygulamalarını kullanarak anlaşılması bakımından çarpıcı bir örnek. Program çıktısını Postscript olarak üretiyor.

Söz konusu grafik yapılar pek çok matematikçinin ve diğer bilim insanlarının da hayranlığını kazanan Hollandalı meşhur sanatçı M. C. Escher'in yapıtlarından esinlenerek hazırlanmış.

Lisp Çalışma Grubu Etkinliklerine Başladı

FZ

İstanbul Bilgi Üniversitesi'nden Bilgisayar Bilimleri bölümü asistanlarının inisiyatifi ile kurulan ve tüm ciddi heveslilere açık olan (İstanbul Bilgi Üniversitesi Lisp Çalışma Grubu) bundan kısa bir süre önce kuruldu ve faaliyetlerine başladı.