8 Bitlik Bir Kafeste Neden Daha Yaratıcıydık?

Bugünün bilgisayarları bol keseden dağıtılan kaynaklar ülkesi: gigabaytlarca bellek, sayısız işlemci çekirdeği, neredeyse sınırsız depolama ve tek tuşla erişilen güçlü kütüphaneler. Buna karşılık eski makineler, örneğin Commodore 64, ZX Spectrum ya da ilk Macintosh bilgisayarlar, programcıya oldukça kısa bir liste sunardı: az bellek, yavaş işlemci, sınırlı renk, kısıtlı ses kanalı ve çoğu zaman acımasız bir ekran çözünürlüğü. İlk bakışta bunlar yalnızca teknik engeller gibi görünür. Oysa retro bilişimin asıl dersi şudur: Kısıt, kodu sadece verimli olmaya değil, karakter sahibi olmaya da zorlar.

Her baytın bir kişiliği vardı

Modern yazılım geliştirmede birkaç kilobaytlık israf çoğu zaman fark edilmez. Eski sistemlerde ise tek bir bayt, ekrandaki bir karakter, müzikteki bir nota, oyundaki bir düşman ya da programın hayatta kalması arasındaki fark olabilirdi. Bu nedenle programcı, veriyi depolamakla yetinmez; onu sıkıştırır, yeniden yorumlar, birden fazla iş için kullanırdı. Aynı bellek bölgesi bir anda hem görsel veri hem ses tablosu hem de geçici çalışma alanı hâline gelebilirdi. Bu, sıradan optimizasyon değil; kaynakların rol değiştirdiği bir sahne sanatıdır.

Örneğin 8 bitlik bir makinede hareketli bir arka plan yaratmak için ekranın tamamını sürekli yeniden çizmek çoğu zaman imkânsızdı. Çözüm, donanım kayıtlarını doğru anda değiştirmek, ekran taramasının belirli satırlarında renkleri dönüştürmek veya karakter setini kaydırmaktı. Seyirci dalgalanan bir deniz, parlayan bir ufuk ya da devinen bir uzay manzarası görürdü. Makine ise aslında birkaç değeri hesaplı bir ritimle değiştiriyordu. Etki büyük, araç küçük, fikir keskin olmalıydı.

Kod, matematikten görsel kompozisyona geçince

Bu ortamda algoritma estetikten ayrı düşünülemez. Bir sinüs tablosu yalnızca matematiksel bir araç değildir; yumuşak bir logo hareketi, titreşen bir yıldız alanı veya akıcı bir karakter animasyonudur. Bit işlemleri sadece hız kazandırmaz; piksel dilini kurar. Döngü sayacı, zamanlama aygıtına dönüşür. Assembly diliyle yazılmış iyi bir demo, donanımın sınırlarını zorlayan teknik bir belge olmaktan çok, işlemcinin nabzına göre bestelenmiş görsel-işitsel bir şiir gibidir.

Özellikle demoscene kültürü bu yaklaşımın en parlak örneğidir. Programcılar, müzisyenler ve piksel sanatçıları birkaç kilobayta müzik, geçiş, üç boyut yanılsaması ve tipografi sığdırmak için yarıştı. Buradaki amaç yalnızca daha küçük dosya üretmek değildi. Amaç, herkesin imkânsız sandığı şeyi çalışan bir illüzyona çevirmekti. Kısıtın estetik değeri tam da burada doğar: Sınır görünür olduğunda, onu aşmak için geliştirilen hile de görünür hâle gelir.

Bol kaynak, az dikkat

Bu, modern teknolojinin değersiz olduğu anlamına gelmez. Güncel araçlar erişilebilirliği, üretim hızını ve deneysel alanı muazzam biçimde genişletmiştir. Ancak bolluk, tasarım kararlarını ertelemeyi kolaylaştırır. Daha fazla bellek kullanmak, daha ağır bir varlık yüklemek ya da daha güçlü donanıma güvenmek çoğu zaman yeterlidir. Retro bilişim ise tersini öğretir: Önce bütçeyi belirle, sonra fikri ona göre arıt. Her özellik maliyetlidir; her efekt bir takas içerir; her çözümün görünmeyen bir bedeli vardır.

Bu yüzden eski sistemlerle uğraşmak nostaljik bir kaçıştan fazlasıdır. Kısıtlılık sanatı, programcıya dikkat ekonomisini öğretir. Az sayıda renk daha bilinçli bir palet doğurur. Az sayıda ses kanalı daha güçlü bir melodi ister. Az bellek daha iyi veri modelleri gerektirir. Az işlem gücü ise daha zarif algoritmalar çağırır. En iyi retro kod, makinenin yoksulluğunu saklamaz; onu sahnenin başrolüne dönüştürür. Belki de yaratıcılığın en sağlam formülü budur: Elinizdekinin azlığını bahane değil, biçim yapın.

Bir Göstericiyi Dereference Etmek: Zihnin Adresine Ulaşabilir miyiz?

C ve C++ dünyasında bir gösterici, sıradan bir değişken değildir: Değerin kendisini değil, onun bellekteki adresini taşır. Bu küçük teknik ayrım, programcının eline etkileyici bir güç verir. Bir sayıyı kopyalamak yerine onun bulunduğu yere gidebilir, oradaki veriyi doğrudan değiştirebilir, hatta yanlış bir hamleyle programı çöküşe sürükleyebiliriz. Göstericiler bu yüzden hem verimliliğin hem de sorumluluğun simgesidir. İlginç olan şu: Zihin felsefesindeki fizikselizm tartışması da benzer bir soruyu sorar. Düşünceye, acıya, hatıraya ve benlik duygusuna doğrudan eriştiğimizi sanırken, aslında yalnızca onları üreten fiziksel adresleri mi okuyoruz?

Adres ile anlam aynı şey değildir

Fizikselizmin temel iddiası nettir: Zihinsel olan her şey, nihayetinde fiziksel süreçlerden oluşur ya da onlara bütünüyle bağlıdır. Bir anıyı hatırladığımızda nöron ağları etkinleşir; korktuğumuzda hormonlar salgılanır; karar verdiğimizde elektriksel ve kimyasal süreçler belirli örüntüler izler. Bu bakış açısından bilinç, gizemli ve maddeden bağımsız bir misafir değil, beynin son derece karmaşık çalışmasının ortaya çıkan niteliğidir. Tıpkı ekrandaki uygulamanın, işlemcideki komutlar ve bellekteki veri düzenleri olmadan var olamaması gibi.

Fakat bir bellek adresini bilmek, o adreste duran verinin insanî anlamını kavramak değildir. Bir programcı 0x7ffe... benzeri bir adres görerek orada bir tamsayı, karakter dizisi ya da nesne bulunduğunu inceleyebilir. Ancak adresin kendisi “bu veri neden önemlidir?” sorusunu cevaplamaz. Benzer biçimde, bir nörobilimci belirli bir beyin bölgesinin etkinliğini ölçebilir; ama bu ölçüm, sevilen bir şarkının insanda uyandırdığı özlemin nasıl hissettirdiğini tek başına vermez. Fiziksel açıklama ile yaşantısal açıklama burada birbirine rakip olmaktan çok, farklı soyutlama katmanları gibi görünür.

Dereference işlemi ve indirgeme arzusu

Bir göstericiyi dereference etmek, yani adresi izleyip doğrudan değere erişmek, teknik olarak güçlü bir işlemdir. Fizikselist indirgemecilik de zihnin “adresini” bulmak ister: Şu duygu hangi devrede, şu inanç hangi örüntüde, şu karar hangi sinirsel mekanizmada gerçekleşiyor? Bu arayış meşrudur; depresyon tedavilerinden beyin-bilgisayar arayüzlerine kadar pek çok ilerleme, zihinsel deneyimlerin bedensel altyapısını ciddiye almakla mümkün olmuştur.

Yine de programlamadaki vahşi gösterici problemi uyarıcı bir metafor sunar. Geçersiz ya da başlatılmamış bir adrese erişmeye kalkışmak, tanımsız davranış üretir. Zihin hakkında aceleci sonuçlar da benzer bir risk taşır. “Beyinde şu bölge aydınlandı, demek ki aşk yalnızca budur” demek; bir metin dosyasının baytlarını gösterip romanın yalnızca ASCII kodlarından ibaret olduğunu söylemeye benzer. Doğru ama yetersizdir. Kodlar romandan bağımsız değildir; fakat romanın konusu, ritmi ve etkisi de yalnızca kod listesine indirgenerek anlaşılmaz.

Fiziksel olmak, basit olmak değildir

Bu paralellik fizikselizmi çürütmez; onu daha dikkatli hale getirir. Zihin fiziksel olabilir, fakat fiziksel bir sistemin yüksek düzeyli düzeni, kendi açıklama dilini gerektirebilir. Yazılım donanımda çalışır; buna rağmen algoritma, veri yapısı, kullanıcı deneyimi ve hata ayıklama gibi kavramlar transistor fiziğine çevrilmeden de vazgeçilmezdir. Bilincin de nöral temeli olabilir; ancak niyet, anlam, öznel deneyim ve sorumluluk kavramları bu temeli inkâr etmeden özerk bir açıklama düzeyinde kalabilir.

Göstericiler bize iki ders verir: Derine inmek değerlidir, ama her derinlik aynı türden açıklama sunmaz. Bellek adresine erişmek kontrol sağlar; anlamı garanti etmez. Beynin fiziksel haritasını çıkarmak da zihnin bütün sırlarını otomatik olarak çözmez. Belki asıl bilgelik, zihni ne maddeden kopuk bir hayalet ne de tek satırlık bir makine kodu saymaktır. İnsan zihni, fiziksel bir evrende çalışan; fakat kendi hikâyelerini, sorularını ve değerlerini üreten olağanüstü karmaşık bir sistemdir.

Az Kod, Çok Ustalık: Neden Kısa Çözümler Daha Zordur?

Programlamada uzun kod bazen çok çalışıldığının kanıtı gibi görünür. Ekranı satır satır dolduran koşullar, döngüler ve yardımcı fonksiyonlar insana üretkenlik hissi verir. Oysa deneyimli geliştiriciler çoğu zaman tersine bir hedefe yönelir: Aynı işi daha az kodla, daha açık biçimde ve daha az hata olasılığıyla yapmak. Bu, tembellik değil; problemin özünü ayıklama sanatıdır. Kısa kod yazmak, tuşlara daha az basmak değil, düşünceye daha fazla emek vermektir.

Satır sayısı değil, zihinsel yük

Bir programın gerçek maliyeti onu ilk yazarken değil, ikinci kez anlamaya çalışırken ortaya çıkar. Kodun bakımını yapan kişi çoğu zaman onu yazan kişi değildir; hatta çoğu projede bu kişi, birkaç ay sonraki sizsiniz. Her gereksiz değişken, tekrar eden kontrol ve özel durum, okuyucunun zihninde taşınması gereken ek bir yük yaratır. Dil ekonomisi tam burada devreye girer: Kod, makinenin çalıştıracağı talimatların yanında insanların okuyacağı bir metindir. İyi kod, yalnızca doğru sonucu üretmez; niçin doğru olduğunu da görünür kılar.

Örneğin aynı doğrulama mantığını beş farklı yerde kopyalamak ilk anda hızlıdır. Fakat kurallar değiştiğinde beş noktayı hatasız güncellemek gerekir. Bu tekrarları anlamlı bir fonksiyonda toplamak, satır sayısını azaltırken değişimin maliyetini de düşürür. Ustalık, tekrar eden şekilleri fark etmek ve onları doğru soyutlama düzeyinde ifade etmektir. Ne çok genel ne de aşırı özel bir yapı seçmek gerekir. Çünkü her soyutlama, gelecekteki değişiklikler hakkında yapılmış bir bahistir.

Kısalık ile sıkıştırma aynı şey değildir

Burada önemli bir tuzak vardır: Daha az kod her zaman daha iyi kod değildir. Bir satıra sığdırılmış, iç içe geçmiş koşullarla dolu bir ifade kısa olabilir ama anlaşılır olmayabilir. Akıllıca yazılmış kod ile numara yapan kod arasındaki fark, açıklıkta görülür. Bir fonksiyon üç satırda işi çözüyor diye başarılı sayılmaz; adı davranışını anlatıyor, girdileri öngörülebilir ve yan etkileri sınırlıysa değerlidir. Dil ekonomisinin amacı karakter sayısını düşürmek değil, gereksiz kavramsal hareketleri azaltmaktır.

Bu nedenle iyi programcı, hazır kütüphaneleri tanır ve tekerleği yeniden üretmez. Sıralama, filtreleme, tarih işlemleri ya da güvenli veri doğrulama için olgun araçlar varken yüzlerce satır özel çözüm yazmak çoğu zaman cesaret değil, maliyet üretmektir. Ancak kütüphane kullanmak da körü körüne kısaltma değildir. Aracın karmaşıklığını, sınırlarını, performansını ve hata davranışını anlamak gerekir. Az kodun arkasında çoğu kez çok bilgi vardır.

Silmek, eklemekten daha zor olabilir

Bir özelliği eklemek görünür bir başarıdır; gereksiz bir özelliği, bağımlılığı veya soyutlamayı silmek ise daha sessiz bir zaferdir. Silmek için sistemin hangi parçalarının gerçekten gerekli olduğunu bilmek gerekir. Bu yüzden refaktörizasyon, estetik bir düzenleme değil, bilgi çıkarma işlemidir. Geliştirici kodu sadeleştirirken sistemin değişmezlerini keşfeder: Hangi kurallar vazgeçilmez, hangi dallanmalar geçmişten kalma, hangi veri yapıları aslında aynı kavramı temsil ediyor?

Sonuçta dil ekonomisi, minimalizm modasından çok mühendislik disiplinidir. En iyi çözüm her zaman en kısa olan değildir; en az sürpriz yaratan, en kolay doğrulanan ve değişime en sakin tepki veren çözümdür. Daha az kod yazmak, problemi küçültmek anlamına gelmez. Tam tersine, problemi yeterince derin anlayıp onun gereksiz kabuğunu soymaktır. Usta programcı, karmaşıklığı ekrana yaymak yerine tasarım kararlarında çözmeye çalışır. Makineye az şey söyleyebilmek için önce probleme çok şey sormak gerekir.

Kodun Gizli Politikası: Nesneler mi Yönetir, Fonksiyonlar mı Özgürleştirir?

Programlama paradigmaları ilk bakışta yalnızca teknik tercihler gibi görünür: sınıf mı yazacağız, saf fonksiyon mu? Fakat kod yazma biçimimiz, dünyayı düzenleme biçimimiz hakkında da ipuçları taşır. Nesne yönelimli programlama ile fonksiyonel programlama arasında kurulabilecek siyasi benzetme, elbette “bir paradigma doğrudan şu ideolojidir” kadar kaba değildir. Yine de her ikisinin de güç, sorumluluk, değişim ve sınır kavramlarını farklı biçimde ele alması dikkat çekicidir.

Nesne yönelimli dünya: Yetki, sınır ve hiyerarşi

Nesne yönelimli programlamada dünya, kimliği olan varlıklardan oluşur. Bir BankaHesabı nesnesinin bakiyesi vardır; bu bakiye dışarıdan gelişigüzel değiştirilemez. Para yatırma ya da çekme gibi işlemler, nesnenin kendi kuralları içinden geçmelidir. Bu yaklaşımın temel kavramları olan kapsülleme, kalıtım ve çok biçimlilik; yetkinin belirli merkezlerde toplanmasını, sorumluluğun rollere bölünmesini ve üst-alt ilişkilerinin modellenmesini mümkün kılar.

Buradaki hiyerarşi her zaman kötü bir şey değildir. Bir hava trafik kontrol sisteminde ya da karmaşık bir oyun motorunda, hangi bileşenin neyi yönetebildiğinin açık olması büyük bir avantajdır. Nesneler, kendi durumlarının bekçisi olur. “Bu veriye kim dokunabilir?” sorusu, mimarinin merkezine yerleşir. Siyasi dilde buna kurumlar, yetki alanları ve temsil mekanizmaları diyebilirdik. Düzen, serbestçe dolaşan veriden değil; sınırları tanımlanmış aktörlerden doğar.

Ancak bu düzenin bedeli vardır. Nesneler birbirlerinin durumuna bağımlı hale geldikçe sistemin görünmeyen ipleri çoğalabilir. Bir metodun sonucu, çağrıldığı anda nesnenin hangi ruh halinde olduğuna bağlı olabilir. Küçük bir değişiklik, uzak bir sınıfta beklenmedik bir sonuç yaratabilir. Bürokratik bir kurumda olduğu gibi, sorumluluk nettir ama karar süreçleri zamanla ağırlaşabilir.

Fonksiyonel dünya: Saflık, eşitlik ve izlenebilirlik

Fonksiyonel programlama ise başka bir teklif sunar: Veriyi dönüştür, fakat gizlice değiştirme. Aynı girdiye aynı çıktıyı veren saf fonksiyonlar, programın davranışını daha öngörülebilir kılar. Bir fonksiyonun sonucu, küresel bir değişkene, gizli bir sayaç değerine veya başka bir nesnenin o anki durumuna bağlı değilse, onu test etmek ve anlamak kolaylaşır.

Bu yapının siyasi benzerliği, merkezi otoriteden çok kuralların şeffaflığına dayanan bir düzen fikrinde bulunabilir. Fonksiyonlar birbirine emir vermez; girdi alır, çıktı üretir. Veri çoğu zaman değiştirilemez kabul edilir. Böylece bir işlem, ortak kaynağı sessizce tüketmek ya da bozmak yerine yeni bir değer üretir. Yan etkisizlik, burada teknik bir temizlik takıntısı değil, güven üretme yöntemidir: Bir parçanın ne yaptığını görmek için bütün sistemi gözetlemek zorunda kalmazsınız.

Fakat fonksiyonel yaklaşım da ütopya değildir. Gerçek dünya yan etkilerle doludur: Dosya yazılır, ağ isteği atılır, ödeme alınır, sensör okunur. Saflığın dışına çıkmak kaçınılmazdır. Fonksiyonel tasarımın başarısı, yan etkileri yok saymasında değil, onları sınırlandırıp görünür hale getirmesindedir. Bir bakıma mesele iktidarı ortadan kaldırmak değil, iktidarın nerede devreye girdiğini kayıt altına almaktır.

Asıl soru: Hangi düzen hangi probleme uygun?

Bu iki paradigma arasındaki seçim, ideolojik bir sadakat testi olmamalıdır. Karmaşık ve uzun ömürlü varlıkların davranışlarını modelleyen bir alanda nesne yönelimli yaklaşım doğal gelebilir. Veri akışlarının, paralel hesaplamaların ve güvenilir dönüşümlerin öne çıktığı yerde fonksiyonel araçlar daha güçlü olabilir. Modern yazılımın en verimli tavrı çoğu zaman hibrittir: Nesneler sınırları ve iş kurallarını taşır; fonksiyonlar bu sınırlar içinde hesaplamayı sadeleştirir.

Yine de bu benzetme bize değerli bir soruyu hatırlatır: Kodumuzda güç nerede toplanıyor? Hangi bileşen değişiklik yapabiliyor, hangisi yalnızca hesaplıyor, hangi etkiler görünmeden yayılıyor? İyi mimari, yalnızca çalışan kod üretmez. Yetkiyi anlaşılır, değişimi denetlenebilir ve sonuçları izlenebilir kılar. Belki de programlamanın en politik yanı budur: Düzen kurarız; sonra o düzen, bizim yerimize karar vermeye başlar.

Rekabetçi Programcı Serisi

Rekabetçi programcı kitabının özetini hazırladım. Antti Laaksonen bu konuda gerçekten çok güzel bir kitap yazmış. Özellikle bilgisayar olimpiyatları ve algoritmalarla ilgilenenler için kitaptan bazı seçme sayfaları aşağıda veriyorum.

Örneğin zaman karmaşıklığı ve bunun hesapları oldukça önemlidir. Örneğin yine tam arama algoritmaları önemli konulardan.

Günümüzde artık bir çok program kodunu veya yazılımı yapay zekaya yaptırabilirsiniz. Fakat algoritma kurmak veya olimpiyat hazırlıklarında yapay zekanın yardımından çok sizin düşünce biçimini değiştirmeniz gerekiyor. Bunun için de bol okumalı, kod yazmalı, problem çözmelisiniz.