JavaScript Fonksiyonel Programlama Temelleri

JavaScript Fonksiyonel Programlama Temelleri
Fonksiyonel programlama (FP), hesaplamaları matematiksel fonksiyonların değerlendirilmesi olarak ele alan bir paradigmadır. JavaScript, birinci sınıf fonksiyonlar (first-class functions), closure'lar ve yüksek seviye fonksiyonlar (higher-order functions) ile fonksiyonel programlamayı güçlü şekilde destekler. Bu yazıda saf fonksiyonlar, immutability, currying, compose ve fonksiyonel pattern'leri inceliyoruz.
1. Saf Fonksiyonlar (Pure Functions)
Saf fonksiyon, aynı girdiler için her zaman aynı çıktıyı döner ve yan etkisi (side effect) yoktur. Test edilebilirlik ve öngörülebilirlik açısından saf fonksiyonlar temel taşıdır.
// ✅ Saf fonksiyon — yan etkisi yok, deterministik
function toplam(a, b) {
return a + b;
}
function indirimUygula(fiyat, oran) {
return fiyat * (1 - oran);
}
function diziFiltreele(dizi, koşul) {
return dizi.filter(koşul);
}
// ❌ Saf olmayan fonksiyonlar
let sayac = 0;
function artir() {
sayac++; // Dış durum değişikliği (yan etki)
return sayac;
}
function rastgeleSayi() {
return Math.random(); // Deterministik değil
}
function logla(mesaj) {
console.log(mesaj); // I/O yan etkisi
return mesaj;
}
// Saf olmayan fonksiyonu saf hale getirme
// ❌ Saf değil
function vergiHesapla(urunler) {
const oran = getVergiOrani(); // Dış bağımlılık
return urunler.map(u => u.fiyat * oran);
}
// ✅ Saf
function vergiHesaplaSaf(urunler, oran) {
return urunler.map(u => ({ ...u, vergiliFiyat: u.fiyat * (1 + oran) }));
}
2. Immutability (Değişmezlik)
Fonksiyonel programlamada veri yapıları değiştirilmez, bunun yerine yeni kopyalar oluşturulur.
// ❌ Mutasyon
const kullanici = { ad: "Tarık", yas: 30 };
kullanici.yas = 31; // Orijinal nesne değişti
// ✅ Immutable güncelleme
const guncellenmis = { ...kullanici, yas: 31 };
// İç içe obje güncelleme
const durum = {
kullanici: { ad: "Tarık", profil: { sehir: "İstanbul" } },
ayarlar: { tema: "koyu" }
};
const yeniDurum = {
...durum,
kullanici: {
...durum.kullanici,
profil: { ...durum.kullanici.profil, sehir: "Ankara" }
}
};
// Array immutable işlemleri
const sayilar = [1, 2, 3, 4, 5];
// ❌ Mutasyon
sayilar.push(6);
sayilar.splice(1, 1);
// ✅ Immutable
const eklenmis = [...sayilar, 6];
const silinmis = sayilar.filter((_, i) => i !== 1);
const guncellenen = sayilar.map((s, i) => i === 2 ? 99 : s);
// Object.freeze ile yüzeysel dondurma
const sabitler = Object.freeze({
API_URL: "https://api.example.com",
VERSION: "1.0.0"
});
// sabitler.API_URL = "x"; // Sessizce başarısız (strict mode'da hata)
// structuredClone ile derin kopyalama (modern)
const derinKopya = structuredClone(durum);
toSorted(), toReversed(), toSpliced() ve with() metotları, diziler üzerinde immutable işlemler yapmayı kolaylaştırır. structuredClone() ise derin kopyalama için en modern ve güvenilir yöntemdir.
3. Currying ve Partial Application
Currying, çok parametreli bir fonksiyonu tek parametreli fonksiyonlar zincirine dönüştürür. Partial application ise bazı parametreleri önceden sabitler.
// Currying
const curry = (fn) => {
const arity = fn.length;
return function curried(...args) {
if (args.length >= arity) {
return fn(...args);
}
return (...sonrakiArgs) => curried(...args, ...sonrakiArgs);
};
};
// Kullanım
const topla = curry((a, b, c) => a + b + c);
console.log(topla(1)(2)(3)); // 6
console.log(topla(1, 2)(3)); // 6
console.log(topla(1)(2, 3)); // 6
// Pratik currying örnekleri
const filtrele = curry((koşul, dizi) => dizi.filter(koşul));
const buyuktenKucuge = curry((alan, a, b) => b[alan] - a[alan]);
const aktifOlanlar = filtrele(k => k.aktif);
const notaGoreSirala = buyuktenKucuge("not");
const ogrenciler = [
{ ad: "Ali", not: 85, aktif: true },
{ ad: "Veli", not: 42, aktif: false },
{ ad: "Ayşe", not: 95, aktif: true }
];
const aktifBasarili = aktifOlanlar(ogrenciler);
// [{ ad: "Ali", ... }, { ad: "Ayşe", ... }]
// Partial application
function partial(fn, ...oncekiArgs) {
return (...sonrakiArgs) => fn(...oncekiArgs, ...sonrakiArgs);
}
const log = (seviye, zaman, mesaj) => `[${seviye}] ${zaman}: ${mesaj}`;
const bilgiLog = partial(log, "INFO");
const hataLog = partial(log, "ERROR");
console.log(bilgiLog("10:30", "Sunucu başlatıldı"));
4. Fonksiyon Kompozisyonu
// compose — sağdan sola
const compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)));
// pipe — soldan sağa (daha okunabilir)
const pipe = (...fns) =>
fns.reduce((f, g) => (...args) => g(f(...args)));
// Pratik örnek: Veri dönüştürme pipeline
const kullanicilar = [
{ ad: "tarık tunç", yas: 30, aktif: true },
{ ad: "ayşe yılmaz", yas: 17, aktif: true },
{ ad: "mehmet demir", yas: 25, aktif: false },
{ ad: "fatma kaya", yas: 22, aktif: true }
];
const basHarfBuyut = (str) =>
str.split(" ").map(s => s[0].toUpperCase() + s.slice(1)).join(" ");
const isleKullanicilar = pipe(
// 1. Aktif olanları filtrele
(liste) => liste.filter(k => k.aktif),
// 2. Yaş kontrolü (18+)
(liste) => liste.filter(k => k.yas >= 18),
// 3. İsim formatla
(liste) => liste.map(k => ({ ...k, ad: basHarfBuyut(k.ad) })),
// 4. Yaşa göre sırala
(liste) => [...liste].sort((a, b) => a.yas - b.yas)
);
const sonuc = isleKullanicilar(kullanicilar);
// [{ ad: "Fatma Kaya", yas: 22 }, { ad: "Tarık Tunç", yas: 30 }]
// Fonksiyonel utility'ler
const prop = (anahtar) => (nesne) => nesne[anahtar];
const not = (fn) => (...args) => !fn(...args);
const head = (dizi) => dizi[0];
const tail = (dizi) => dizi.slice(1);
const uniq = (dizi) => [...new Set(dizi)];
Sonuç
Fonksiyonel programlama prensipleri — saf fonksiyonlar, immutability, currying ve fonksiyon kompozisyonu — JavaScript kodunuzun test edilebilirliğini, okunabilirliğini ve güvenilirliğini artırır. Bu prensipleri günlük geliştirme pratiğinize entegre ederek daha sağlam ve bakımı kolay uygulamalar geliştirebilirsiniz.