Fonksiyonel Programlamaya Giriş: Map, Reduce ve İmmutability Örnekleri
Fonksiyonel Programlamaya Giriş
Fonksiyonel Programlamaya Giriş: Map, Reduce ve İmmutability Örnekleri

Fonksiyonel programlama (FP), “veriyi adım adım değiştirmek” yerine “veriden yeni değerler üretmek” fikrine dayanır. Tamamen FP yazmak zorunda değilsiniz; ama map, reduce ve immutability (değişmezlik) gibi yapı taşları, daha tutarlı veri dönüşümleri yazmanıza yardımcı olur.
Bu rehberde JavaScript ve Python üzerinden pratik örneklerle ilerleyeceğiz. Amaç; kavramları ezberlemek değil, ne zaman kullanacağınızı ve ne zaman kaçınmanın daha iyi olacağını netleştirmek.
Fonksiyonel yaklaşımın 3 temel fikri
1) Saf (pure) fonksiyon: aynı girdi, aynı çıktı
Saf fonksiyon ideal olarak (1) aynı girdiye aynı çıktıyı üretir ve (2) dış dünyayı (global değişken, dosya, ağ vb.) değiştirmez. Her yerde mümkün olmasa da, özellikle veri dönüştürme katmanlarında saf fonksiyonlar kodu sadeleştirebilir.
Takeaway: Dönüşümleri saf tutabildiğiniz ölçüde test yazmak ve davranışı tahmin etmek kolaylaşır.
2) Higher-order functions: fonksiyon alan / döndüren fonksiyonlar
map ve reduce, fonksiyon alıp koleksiyon üzerinde çalışan klasik higher-order örnekleridir: dönüşüm mantığını bir fonksiyon olarak verirsiniz, işlem “koleksiyona uygulanır”.
Takeaway: Dönüşüm mantığını küçük fonksiyonlara ayırmak, okuması kolay akışlar üretir.
3) Immutability: var olanı değiştirme, yenisini üret
İmmutability (değişmezlik), akademik tanımıyla “bir değer oluşturulduktan sonra değiştirilememesi” yaklaşımıdır. Princeton ders notlarında şu şekilde ifade edilir: “A data type is immutable if you can’t change a data-type value once created.” (Kaynak: Princeton: Designing Data Types.)
- Pratikte ne kazandırabilir? Paylaşılan veriyi “yerinde değiştirmek” yerine yeni değer üretmek, yan etkiyi sınırlamaya ve değişikliklerin izini sürmeye yardımcı olabilir.
- Bedeli ne olabilir? Daha fazla kopyalama/nesne üretimi, özellikle büyük verilerde maliyet yaratabilir.
Takeaway: Değişmezlik, her yerde katı bir kural değil; kritik veri akışlarında yan etkileri azaltmak için bir araçtır.
JavaScript’te map(): bir diziden yeni bir dizi üretmek
MDN’ye göre Array.prototype.map(), dizideki her eleman için bir fonksiyon çalıştırır ve sonuçlardan yeni bir dizi döndürür. Kaynak: MDN Array.prototype.map().
map ne zaman ideal?
- Bir listeyi aynı uzunlukta başka bir listeye dönüştürmek istediğinizde (ör. fiyatları vergi ekleyerek dönüştürmek).
- UI için view model üretirken.
- API’den gelen ham veriyi “temizleyip” yeni formata çevirirken.
JavaScript map örneği (dönüşüm)
Girdi: [10, 20, 30]
Hedef: Her elemanı 2 ile çarpıp yeni dizi üretmekKod:
const numbers = [10, 20, 30];
const doubled = numbers.map(n => n * 2);
// doubled: [20, 40, 60]
// numbers: [10, 20, 30]
- İpucu: Dönüşümünüz tek cümleyle anlatılamıyorsa, callback’i ayrı bir fonksiyon olarak isimlendirin.
Takeaway: map, “liste → liste dönüşümü” için en anlaşılır seçeneklerden biridir.
map kullanırken sık yapılan hatalar
- Yan etki için map kullanmak: Sadece log basmak veya dışarıdaki bir diziyi doldurmak için map tercih edilmez.
- Return unutmak: Blok gövdeli arrow function’larda return unutulabilir.
JavaScript’te reduce(): bir diziyi tek bir değere indirmek
Array.prototype.reduce(), diziyi tek bir sonuç değerine “indirgemek” için kullanılır. MDN, accumulator (biriktirici) mantığını ve initialValue (başlangıç değeri) parametresini açıklar. Kaynak: MDN Array.prototype.reduce().
reduce’u zihinde canlandırma
reduce’u, “Bir kutu (accumulator) var; listedeki her elemanı sırayla alıp bu kutuyu güncelliyorum; en sonda kutuda tek bir sonuç kalıyor.” şeklinde düşünebilirsiniz.
Takeaway: reduce, “biriktire biriktire tek sonuca gitme” kalıbını geneller.
Önemli davranış: boş dizi + initialValue
- MDN’nin açıkladığı davranışa göre, boş bir dizi üzerinde initialValue vermeden reduce çağırmak hata (TypeError) oluşturur.
- Bu yüzden reduce yazarken initialValue seçimini bilinçli yapmak iyi bir alışkanlıktır.
reduce neden güçlü, neden riskli?
- Güçlü: Toplam, çarpım, en büyük değer, sözlük/harita (object) üretme, gruplama gibi işleri tek bir şablonda toplar.
- Riskli: Her şeyi tek bir reduce bloğuna sıkıştırmak, okunabilirliği düşürebilir. Bu durumda işlemi map/filter gibi daha açık adımlara bölmek çoğu zaman daha anlaşılır olur.
JavaScript reduce örneği 1: toplam
Kod:
const cartTotals = [19.99, 5.00, 12.50];
const sum = cartTotals.reduce((acc, price) => acc + price, 0);
// sum: 37.49
Takeaway: Toplam gibi tek sonuç üreten işlemlerde reduce, doğru initialValue ile nettir.
JavaScript reduce örneği 2: gruplama (group by)
Girdi: Kullanıcıları role göre grupla
Kod:
const users = [
{ name: 'Ada', role: 'admin' },
{ name: 'Linus', role: 'user' },
{ name: 'Grace', role: 'admin' }
];
const byRole = users.reduce((acc, u) => {
const key = u.role;
const existing = acc[key] ?? [];
return { ...acc, [key]: [...existing, u] };
}, {});
- Bu örnekte her adımda yeni obje ve yeni dizi üreterek “mutasyon yapmama” yaklaşımını koruyoruz.
- Bu stil, bazı kod tabanlarında daha öngörülebilir bir akış sağlayabilir; ancak ek kopyalama maliyeti de doğurabilir. İhtiyaç varsa ölçüm yapın.
Takeaway: reduce ile karmaşık sonuçlar üretmek mümkün; ama okunabilirlik ve maliyet dengesini gözetin.
reduce ne zaman tercih edilmemeli?
- İşlem birden fazla dönüşüm adımı içeriyorsa ve tek reduce bloğu “her şeyi yapıyorsa”.
- Ekipte yeni başlayanlar varsa ve mantık tek bakışta anlaşılmıyorsa.
- Aslında map/filter veya daha açık bir döngü ile daha net ifade edilebiliyorsa.
map vs reduce: hızlı karşılaştırma
| Kriter | map() | reduce() |
|---|---|---|
| Çıktı tipi | Her zaman yeni bir dizi | Tek bir değer (number, object, array, vb.) |
| En iyi kullanım | Eleman bazlı dönüşüm | Toplama, biriktirme, birleştirme, gruplama |
| Okunabilirlik | Genellikle daha yüksek | Tek blokta büyürse düşebilir |
| Değişmezlik yaklaşımıyla uyum | Doğal olarak uyumlu (yeni dizi üretir) | Uyumlu olabilir; accumulator’ı mutasyona uğratmadan yazmak dikkat ister |
Takeaway: map “dönüştürür”, reduce “birleştirir/indirger”.
Python tarafı: map ve functools.reduce
Python’da reduce, Python 3’te built-in olmaktan çıkarılıp functools modülüne taşınmıştır. Kaynak: Python docs: functools.
Python map örneği
Kod:
numbers = [10, 20, 30]
doubled = list(map(lambda n: n * 2, numbers))
# doubled: [20, 40, 60]
Aynı işi Python’da çoğu zaman daha okunur biçimde şöyle de yazarsınız:
Kod:
doubled = [n * 2 for n in numbers]
Takeaway: Python’da da ana fikir aynı: “girdiyi yerinde değiştirmek yerine yeni çıktı üretmek”.
Python functools.reduce örneği
Kod:
from functools import reduce
cart_totals = [19.99, 5.00, 12.50]
sum_total = reduce(lambda acc, price: acc + price, cart_totals, 0)
# sum_total: 37.49
- Not: Python’da sayıları toplamak için pratikte çoğu zaman sum(cart_totals) daha net bir tercihtir.
Takeaway: reduce, “özel bir biriktirme” gerçekten gerektiğinde değer kazanır.
İmmutability (değişmezlik) pratikte nasıl düşünülmeli?
İmmutability’yi “hiçbir şeyi asla değiştirme” şeklinde katı bir kural gibi değil, kritik veri akışlarında yan etkiyi sınırlama yaklaşımı olarak ele almak çoğu projede daha gerçekçidir.
- Tanım (kaynaklı): Oluşturulan değerin değiştirilememesi (Princeton). Kaynak
- Pratik bakış: Değişmez parçalarla çalışmanın operasyonel/delivery tarafında tekrarlanabilirliği kolaylaştırabileceği fikri, Martin Fowler’ın “immutable server” yaklaşımında tartışılır (uygulama kodundaki immutability ile bire bir aynı konu değildir). Kaynak
Takeaway: Değişmezlik, “değişikliği yönetme” yükünü azaltmaya yönelik bir tasarım tercihi olabilir; kapsamını proje ihtiyaçları belirler.
JavaScript’te immutability örnekleri (temel)
Diziye eleman ekleme (mutasyon yerine yeni dizi):
Kod:
const items = ['a', 'b'];
const nextItems = [...items, 'c'];
// items aynı kalır, nextItems yeni dizidir
Objede alan güncelleme (mutasyon yerine yeni obje):
Kod:
const user = { name: 'Ada', plan: 'free' };
const upgraded = { ...user, plan: 'pro' };
// user aynı kalır, upgraded yeni objedir
Dikkat: iç içe yapılarda “shallow copy” tuzağı
- Spread operatörü çoğu durumda yüzeysel kopya üretir.
- İç içe objelerde alt seviyedeki referanslar paylaşılabilir.
- Doğru strateji (veri şekli, performans ihtiyacı, ekip standardı) projeye göre değişir.
Takeaway: Değişmezlik hedefiniz varsa, iç içe yapılarda kopyalama stratejisini bilinçli seçin.
Adım adım: map/reduce ile daha “fonksiyonel” yazmak için mini kontrol listesi
- 1) Dönüşümünüzü isimlendirin: Karmaşık inline fonksiyonlar yerine küçük yardımcı fonksiyonlar okunabilirliği artırabilir.
- 2) map sadece dönüşüm için: Yan etki (log, dış değişken güncelleme) ağırlıktaysa yaklaşımı sadeleştirin.
- 3) reduce kullanıyorsanız initialValue düşünün: Boş dizi ve tip tutarlılığı açısından kritiktir (MDN).
- 4) Okunabilirliği ölçüt alın: Tek bir reduce bloğu uzuyorsa, işlemi daha küçük adımlara bölün.
- 5) Küçük testlerle doğrulayın: Dönüşüm fonksiyonları küçük girdilerle kolay test edilir.
Takeaway: Amaç “en kısa kod” değil, ekipçe kolay okunan ve değiştirilen kod.
Sonuç: FP bir hedef değil, araç seti
Fonksiyonel programlama, günlük geliştirmede “ya hep ya hiç” yaklaşımı gerektirmez. map ile güvenli dönüşümler yapmak, reduce’u doğru yerde kullanmak ve immutability ile yan etkileri sınırlamak; hem JavaScript’te hem Python’da daha bakımı kolay kod üretmenize katkı sağlayabilir.
Pratik egzersiz: Elinizdeki gerçek bir veri akışını seçin (ürün listesi, kullanıcı etkinlikleri, log kayıtları). Önce döngüyle yazın, sonra map/filter/reduce ile daha küçük parçalara bölerek tekrar yazın. Hangisi ekip arkadaşınızın daha hızlı anlayacağı bir çözümse, çoğu zaman “daha iyi” olan odur.