İmperatif ve Deklaratif Programlama Farkı Anlamak
- Tarık Tunç

- 5 Kas
- 8 dakikada okunur

Selamlar ve hoş geldiniz! 👋
Bu yazı, daha önce kaleme aldığım JS ile Fonksiyonel Programlama serimizin hemen bir devamı olarak karşınızda. O yazıda değinmekten kaçındığım, ama Fonksiyonel Programlamayı (FP) tam anlamıyla anlamamız için kritik olan o derin konuya dalıyoruz.
Amacım, tüm bu kavramları tek bir yazıda toplayıp sizi boğmak yerine, konuları sindirilebilir parçalara ayırmak. 🧠
FP'nin Temel Felsefesi:
Unutmayın, Fonksiyonel Programlama (FP) özünde Declarative (Bildirimci) bir yaklaşımdır. Bu bölümde, FP'ye sağlam bir giriş yapmak için, bu Declarative yöntemin, programlamanın geleneksel yolu olan Imperative (Emirci) yaklaşımdan NASIL ayrıldığını netleştireceğiz.
Hadi başlayalım! Bu karşılaştırmanın, kod yazma şeklinizi nasıl değiştireceğini göreceksiniz.
🎯 DEĞİŞİMİN ANAHTARI: Imperative'den Declarative'e Fonksiyonel Programlama
Selamlar! 👋 Bu yazı, daha önceki JS ile Fonksiyonel Programlama maceramızın hemen devamı. O yazıda girmeye çekindiğim, ama Fonksiyonel Programlamayı (FP) tam anlamıyla anlamamız için şart olan o kritik konuya dalıyoruz: Yöntem Farkları!
Tek bir yazıda her şeyi anlatıp kafaları karıştırmayalım. Adım adım ilerleyelim. 👣
1. ⚙️ Imperative (Emirci) vs. Declarative (Bildirimci): Ne Fark Var?
Fonksiyonel Programlama, aslında Declarative (Bildirimci) bir programlama türüdür. Peki bu, Imperative (Emirci) yaklaşımdan nasıl ayrılıyor?
Bu, patronun bir işi yaptırma şekli gibi.
Kavram | Odak Noktası | Nasıl Anlatır? |
Imperative (Emirci) | NASIL yapılacağı | "Şuraya git, şu malzemeyi al, 5 dakika karıştır, sonra fırına koy." (Adım Adım Talimat) |
Declarative (Bildirimci) | NE yapılacağı | "Bana bir pasta yap." (Sonucu Bildirme) |
Kilit Nokta: Declarative'de sadece ne istediğini söylersin. Kod, nasıl yapılacağını arka planda halleder. Bu, kafa karışıklığını ve gereksiz detayları azaltır. 🧠
2. 📝 Imperative ve Declarative: Örnek Kullanımlar

Hadi bu farkı, en basit kod örnekleriyle görelim. Amacımız: Bir dizideki çift sayıların iki katını almak.
A. Imperative (Emirci) Kullanım:
Bu yaklaşım, adımları TEK TEK kontrol eder.
JavaScript
let sayilar = [1, 2, 3, 4, 5];
let sonuc = []; // Sonucu kendimiz tutmalıyız
// 1. Dizi üzerinde döngü KUR (NASIL yapılacak 1)
for (let i = 0; i < sayilar.length; i++) {
let sayi = sayilar[i];
// 2. Çift mi diye KONTROL ET (NASIL yapılacak 2)
if (sayi % 2 === 0) {
// 3. İkiyle ÇARP ve Ekle (NASIL yapılacak 3 & 4)
sonuc.push(sayi * 2);
}
}
console.log(sonuc); // [4, 8]
⚠️ Not: Imperative kodda, geçici değişkenleri (i, sonuc, sayi) kendimiz yönetmek zorundayız. Bu, zihinsel yükü artırır.
B. Declarative (Bildirimci) Kullanım (FP'ye Yakın):
Bu yaklaşım, modern JavaScript'in Fonksiyonel Programlama metotlarını kullanır.
JavaScript
let sayilar = [1, 2, 3, 4, 5];
// Sadece "Çift olanları filtrele, sonra ikiyle çarp" DİYORUZ (NE istediğimizi söylüyoruz)
let sonuc = sayilar
.filter(sayi => sayi % 2 === 0) // Çift sayıları al
.map(sayi => sayi * 2); // Onları ikiyle çarp
console.log(sonuc); // [4, 8]
✅ Hafiflik: Bu kodda döngü kurmakla veya i değişkenini yönetmekle uğraşmayız. Kod, ne yaptığını daha net söyler.
3. ⏳ Imperative Programlama: Tarihsel Gelişim (Kısa Bir Mola)
Imperative programlama, aslında bilgisayarların doğal dilidir!
İlk Adımlar: Bilgisayarlar, en temelinde emirler serisini (CPU komutları) takip eder.
Temel Yapı: İlk diller (Fortran, C, Assembly), bu adım adım emir yapısına çok yakındı. Bu diller, makineye yakın çalışmanın en verimli yoluydu.
Sonuç: Bu yüzden, yıllardır kod yazarken ilk öğrendiğimiz şeyler hep for döngüleri, if/else komutları gibi Imperative yapılardır.
4. 🚫 Fonksiyonel Programlama: Neden Imperative Yapıları Sevmez?
FP, Declarative olmayı başarmak için özellikle Imperative yapıların bazı özelliklerinden uzak durur.
FP'nin uzak durduğu en önemli iki konu:
Durum Değişikliği (State Mutation): Imperative'de, bir değişkenin değerini sürekli değiştiririz (sonuc.push(), i++). FP'de amaç, bir kere değer atandıktan sonra onu asla değiştirmemektir. Bu, hataları ve beklenmedik sonuçları azaltır.
Yan Etkiler (Side Effects): Bir fonksiyonun, kendi dışındaki bir şeyi değiştirmesi (örneğin, global bir değişkeni güncellemesi). Imperative kod bunu sıkça yapar. FP'de fonksiyonlar saf olmalı: Aynı girdi, her zaman aynı çıktıyı vermeli.
Özetle: Fonksiyonel Programlama, NASIL yerine NE sorusuna odaklanarak, kodu daha tahmin edilebilir, daha temiz ve okuması daha kolay hale getirir. Bu, DEHB'li zihinler için karmaşıklığı sadeleştiren süper bir özelliktir!
🤯 Emir mi, Bildirim mi? Imperative ve Declarative Kavramları Arasındaki Fark
Programlamadaki bu iki dev kavram, aslında çok basit bir soruya odaklanır: "Nasıl?" mı, yoksa "Ne?" mi?
Programlama Stili | Türkçe Anlamı | Kilit Soru | Odak Noktası |
Imperative | Emir/Zorunluluk | NASIL Yapılacak? | Adım adım talimatlar ve detaylar. |
Declarative | Bildiren/Açıklayan | NE Yapılacak? | Sadece istenen sonuç (çıktı). |
Peki, "Nasıl" ile "Ne" arasındaki bu fark, gerçekten bu kadar büyük programlama yaklaşımlarını nasıl doğurur? İşte kafa karışıklığını gideren can alıcı örnekler:
🖍️ Örnek 1: Çizim Programı (Detay vs. Sonuç)
Bir bilgisayara ekranda bir dikdörtgen çizmesini istediğimizi varsayalım.
A. Imperative Yöntem (NASIL Yapılacak):
Bu yöntem, her detayı tek tek emreder. Bir robotu yönetmeye benzer.
kalemiSuPozisyonaGötür()
kalemiBastır()
kalemiSuPozisyonaSürükle()
kalemiSuPosizyondaDurdur()
(... ve dört kenar için bu adımları TEKRARLA ...)
kalemiKaldır()
📝 Imperative Not: İşlemi detaylı bir şekilde, açıklayıcı emirlerle gerçekleştiririz. Bu, uygulama (implementation) detaylarına boğulmak demektir.
B. Declarative Yöntem (NE Yapılacak):
Bu yöntemde sadece ne istediğimizi bildiririz.
diktorgen(ciz)
✨ Declarative Not: Sadece yapılacak şeyi söylüyoruz. Arka planda diktorgen fonksiyonu, kalemi oynatma mantığını bizden soyutlar (gizler). Bu, matematikteki $f(g(x))$ gibi, fonksiyonları birleştirerek kod geliştirmeyi sağlar.
🌐 Örnek 2: Veritabanı İşlemleri (SQL)
Karmaşık sistemlerde, gerçekleştirim (implementation) detaylarını soyutlamak kritik önem taşır. İşte bu yüzden Declarative yaklaşım, DSL (Domain Specific Language - Alana Özgü Dil) gerektiren yerlerde parlar.
Bunun En Parlak Örneği: SQL (Database Processing)
SQL ile kod yazdığınızda (Declarative), siz sadece NE istediğinizi söylersiniz:
select * from students where score > 80
Sizi Kurtardığı Detaylar (Imperative Yükler):
Hangi Diskten Okunacak? (Dosya sistemi detayları)
Veriler Nasıl Sıralanacak? (En hızlı algoritma ne?)
Hangi Bellek Yönetimi Kullanılacak? (Gerçekleştirim detayı)
Sonuç: SQL, sizi veritabanının bir dizi gerçekleştirim detayından kurtarır. Bu üst seviye dil sayesinde, veritabanını kimin (Oracle, PostgreSQL, vs.) ve NASIL yönettiği sizi ilgilendirmez. Siz sadece NE istediğinize odaklanırsınız.
Mükemmel eklemeler! Özellikle HTML, Regex ve Makine Öğrenimi benzetmeleri, Declarative ve Imperative arasındaki soyutlama farkını DEHB okuyucusu için daha somut ve akılda kalıcı hale getiriyor.
Bu ikinci bölümü, önceki akışa entegre ederek, hızlı okuma ve vurgu prensiplerine göre düzenleyelim.
🌐 Declarative Gücü: HTML ve Regex Örnekleri
Declarative yaklaşımın günlük hayatımızdaki gücünü ve bizi NASIL detaylarından nasıl kurtardığını görelim.
1. 🖥️ HTML (Web Sayfası Oluşturma)
HTML, sadece bir işaretleme (markup) dili sunar. Siz tarayıcıya sadece NE göstermek istediğinizi söylersiniz:
HTML
<html>
<head></head>
<body>
<h1>Declarative Programlama</h1>
<p>Lorem ....</p>
<body>
</html>
Sizin İşiniz (Declarative): Bu etiketleri tanımlamak.
Tarayıcının İşi (Imperative): HTML'i okuyup, DOM'u oluşturup, CSS ve JS ile birleştirerek sayfayı NASIL render edeceğini (çizeceğini) halletmek.
🚫 Uğraşmanıza Gerek Yok: HTML sayesinde, sayfanın piksel piksel ekrana nasıl yazılacağı, kullanıcı etkileşimlerinin nasıl yönetileceği gibi alt seviye detaylarla uğraşmak zorunda kalmazsınız.
2. 🔍 Regex (Örüntü/Pattern Arama)
Bir metin içindeki karmaşık bir örüntüyü (pattern) aramak istediğinizde, Declarative olan Regex (Düzenli İfadeler) devreye girer.
JavaScript
// Örnek: $! ... !$ arasına sıkışmış bir metni bul ve sil.
var regex = /\$!(.*?)!\$/g;
return str.replace(regex, “”);
Kod ile Yapsanız: Bu işlemi manuel olarak döngüler ve if/else bloklarıyla yapmaya kalksanız, hem çok sayıda fonksiyon yazmanız gerekir, hem de performanslı olması zorlaşır. (Imperative)
Regex ile: Siz sadece neye benzeyen bir şey aradığınızı söylersiniz. Regex Motoru, bunu en performanslı NASIL bulacağını arka planda halleder.
🧱 Temel Yapı: Üst Seviye Dil + Motor (Engine)
Hem HTML'de, hem Regex'te (aynı zamanda React'in JSX yapısında ve Fonksiyonel Programlamada) hep aynı yapıyı görürüz:
Üst Seviye Alan Dili (Declarative DSL): Öğrenilmesi zorlayıcı olabilir.
Motor/Engine: Bu dilin belirttiklerini karmaşık altyapı gerçekleştirimini yaparak çalıştıran yapı.
Güçlü Yanı: Bu diller, kullanıcıyı belli bir küme içinde kısıtlar. Bu kısıtlama, başlangıçta zor gelse de, uzun vadede çok daha az hataya yol açar.
💡 Imperative'in Hükmettiği Alanlar
Peki, Declarative bu kadar iyiyse, Imperative ne zaman kullanılmalı?
Cevap: Karmaşık İş Akışları ve Uygulama Geliştirme!
Alanı: Business uygulamaları, çok katmanlı, karmaşık iş akışlı uygulamalar.
Neden Imperative: Bu tip iş kurgularını tek bir matematiksel fonksiyonda toplamak mümkün olmaz. Akışın, durumun ve emirlerin adım adım kontrol edilmesi gerekir.
Örnek Diller: Basic, Pascal, Delphi, C, C++, Java, C# gibi diller, bu tip karmaşık akışları daha basit bir şekilde geliştirmenizi, daha sonra daha iyi bakım yapmanızı ve daha okunabilir olmasını sağlar.
🧠 Bonus Benzetme: Makine Öğrenimi (Matematik vs. Prosedür)
Bu iki yaklaşım, istatistiksel modellemelerde (Makine Öğrenimi) bile kendini gösterir:
Fonksiyonel Yaklaşım (Declarative): Verinizin temiz olduğu ve arka planda bir matematiksel formülle (örn: Linear Regresyon, Logistic Regresyon) ifade edilebildiği durumlar.
Imperative Yaklaşım (Procedural/Prosedürel): Veri yapınızın çok karmaşık olduğu ve modelin adım adım emirlerle (Decision Tree/Karar Ağacı gibi prosedürel dallanmalarla) tanımlanması gereken durumlar.
Bu girişle beraber Fonksiyonel Programlamanın neden Declarative olmayı seçtiğini ve bu seçimin kodun kalitesini nasıl artırdığını çok net anladık!
2. 🔀 Imperative ve Declarative: Programlama Paradigmaları
Önceki bölümlerdeki uzun örneklerden sonra, bu iki temel yaklaşımın yazılım dünyasında nasıl yer aldığını hızlıca listeleyelim.
Paradigma | Kilit Özellik | Örnek Diller/Yapılar (DSL) |
Imperative | Adım adım NASIL yapılır. | C, C++, Java, Pascal, Basic (Procedural Diller) |
Declarative | Sadece NE istenir. | SQL, Regular Expression, HTML (DSL/Markup Diller), Fonksiyonel Programlama Dilleri |
Hybrid (Melez) | Her iki yaklaşımı da kullanır. | Javascript, Python |
3. 📜 Imperative Programlama Yaklaşımının Tarihsel Gelişimi

Imperative yaklaşım, aslında programlamanın en eski ve en köklü yoludur. Zaman içinde kendi içinde büyük bir evrim geçirdi.
Imperative, tarihte bu sırayı takip etti:
GOTO Programlama ➡️ Yapısal Programlama (Structured) ➡️ Procedural (Prosedürel) ➡️ Object Oriented (Nesne Yönelimli)
A. 🚀 Başlangıç Noktası: GOTO ve Assembly

Temel: En başta, Assembly (makine dili) kodları, sadece arka arkaya gelen emirlerden (Register'a veriyi taşı, topla, diske yaz) oluşuyordu.
GOTO Tehlikesi: Bu emirlerin akışını değiştirmek için kullanılan GOTO (Git) komutu, kodun bir noktadan diğerine atlamasını sağlıyordu.
Sonuç: Kod, bir süre sonra Spagetti gibi birbirine karıştı, takip edilemez ve bakımı yapılamaz hale geldi. Bu durum, 1968'de Dijkstra tarafından "Go to Statement Considered Harmful" (GOTO Zararlıdır) makalesiyle resmen duyuruldu.
B. 🧱 Yapısal Programlama (Structured Programming)
Yazılımdaki bu karmaşayı yönetmek için GOTO'nun yerine, daha organize ve yapısal kontrol akışları geliştirildi.
Yeni Yapı | Açıklama | Amacı (GOTO Yerine) |
Sequences | Ardı ardına işletilen kod blokları ve Subroutineler. | Kodun akışını lineer tutmak. |
Selections | Sistem Durumuna (State) göre koşullu çalıştırma. | if, switch gibi yapılarla karar verme. |
Iterations | Belirli bir Durum (State) elde edilene kadar döngü kurma. | for, while ile tekrarlayan işlemleri yönetme. |
🔑 Ek Anahtar Kelimeler: İşlem sırasında acil çıkışlar için return, break, continue, exit gibi kelimeler kullanılmaya başlandı.
C. 🧩 Subroutine (Alt Program) Detayları: Farklar
Yapısal Programlamanın merkezindeki Subroutine (alt program), farklı şekillerde karşımıza çıkar:
Statement: Kodun en ufak işletilen satırı. (Örn: a = b + 3;)
Procedure (Prosedür): Bir değer döndürmez. Bir grup işlemi yapar. (Örn: procedure topla(&toplam, a, b){ &toplam = a + b })
Function (Fonksiyon): Bir grup işlemi yapıp, geriye bir değer döndürür. (Örn: function topla(a, b){ return a + b; })
(FP için Önemli!) Pure Function: Dışarıdan aldığı değerler dışında etkilenmez ve dışarıyı etkilemez. (Yan Etkisiz)
Method (Metot): Bir sınıfa (class) bağlı olan fonksiyon.
D. 🚶 Procedural Programlama (Prosedürel)
Bu aşamada programlama, şu iki ana unsur etrafında dönmeye başladı:
Değişkenler ve Veri Yapıları (Veriler)
Subroutineler / Prosedürler (Bu verileri işleyen işlemler)
Odak Noktası: Prosedürler, kendilerine verilen veri yapılarını işler. Sistemin genel bir durumu (State), bu prosedürler arasında değiştirilerek işletilir.
Bu tarihsel yolculuk, Imperative dillerin durumu ve akışı yönetmeye ne kadar odaklandığını gösteriyor. İşte Fonksiyonel Programlama bu durum yönetimi karmaşasından kaçarak, yeni bir çözüm sunuyor.
Hazırsanız, bu serinin son ve en kritik bölümüne geçelim: "Fonksiyonel Programlamanın Imperative Yapıları Kullanmaması" konusunu, özellikle "Saf Fonksiyon" ve "Yan Etkisizlik" kavramlarıyla pekiştirelim mi?
Harika! Artık Imperative yolculuğunun son durağı olan Nesne Yönelimli Programlamayı hızlıca özetleyip, serinin ana fikri olan "Fonksiyonel Programlama Neden Imperative'den Kaçar?" sorusunu netleştirebiliriz.
İşte son bölümlerin, DEHB odaklı netlik ve akışla düzenlenmiş hali:
E. 🧩 Imperative'in Son Durağı: Nesne Yönelimli Programlama (OOP)
Procedural programlamadan sonra gelen OOP, temel olarak Durum (State) yönetimini daha kontrollü hale getirdi.
Odak Noktası: Veri (State/Attribute) ve bu veriyi işleyen metotları bir araya getiren Objeler.
İletişim: Objeler, birbirleriyle arayüzler (interface) üzerinden iletişim kurar.
Gelişim Nedeni: Uygulamalar büyüdükçe (Enterprise/Kurumsal yazılımlar), projelerde daha fazla kişinin çalışması ve kodun soyutlama (abstraction) ve tutarlılık (cohesion) açısından daha iyi yönetilmesi gerekti.
Özet: OOP, genel uygulama kapsamındaki karmaşık durum yönetimini, daha küçük ve kontrol edilebilir objelerin içine taşıdı. Yine de temelinde emirler (metot çağrıları) ve durum değişiklikleri vardır.
4. 🚫 Fonksiyonel Programlamanın Imperative Yapıları Kullanmaması
Serimizin kilit noktasına geldik: Fonksiyonel Programlama (FP), aslında Yapısal (Structured) Programlamadaki işlemleri yapmak ister, ancak Imperative'in getirdiği karmaşadan kaçınır.
FP Neden Kaçar? Çünkü FP, Sistem Durumunu (System State) kullanmadan çalışmayı hedefler.
Imperative Yapı | Fonksiyonel Karşılığı | FP'nin Kaçınma Nedeni |
for Döngüsü (Loop) | forEach, map, reduce gibi metodlar. | Döngü değişkeni (i) ve kontrol akışı karmaşasını siler. |
if/else (Condition) | filter veya Fonksiyonların Birleşimi. | Durumun anlık değişkenliğini azaltır, daha saf bir akış sağlar. |
break, continue | İşlemi döngüden önce veya sonra hazırlama. | Kontrolsüz atlamaları engeller, kodun tahmin edilebilirliğini artırır. |
🔍 Kodda Imperative'den Kaçış
1. Döngüden Kaçış:
Imperative (Kontrol Senin Elinde):
JavaScript
// NASIL yapılacağını sen söylüyorsun:
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
console.log(element);
}
Fonksiyonel (NE İstediğini Söylüyorsun):
JavaScript
// Sadece ne istediğini söylüyorsun:
arr.forEach(el => console.log(el));
// NASIL döneceğini forEach metodu halleder.
2. Acil Çıkıştan Kaçış:
Procedural bir dilde döngüden break ile aniden çıkabilirsin. Fonksiyonel diller buna izin vermez! (forEach tüm elemanları bitirir.)
FP Çözümü: Eğer döngüde istemediğiniz elemanlar varsa, onları döngüye girmeden önce elemeniz gerekir:
JavaScript
// Döngüyü kırmak yerine, önce filtrele:
arr
.filter(el => el.koşulu_sağlar) // Önce istenmeyenleri ELE
.forEach(el => console.log(el)); // Sonra temiz liste üzerinde İŞLEM YAP
🧠 Kilit Fark: FP'de kontrol akışını içeriden yönetmek yerine, veriyi döngüye girmeden önce veya döngüden çıktıktan sonra fonksiyonlar aracılığıyla dönüştürürsünüz. Bu, "Bütün Elemanlar Üzerinde Çalış" felsefesini destekler ve kodun okunurluğunu artırır.



