İçeriğe geç
JavaScript

JavaScript Fonksiyonel Programlama Temelleri

Tarık Tunç
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);
İpucu: ES2023 ile gelen 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)];
Uyarı: Aşırı fonksiyonel soyutlama (point-free tarzı), kodun anlaşılmasını zorlaştırabilir. Okunabilirliği her zaman ön planda tutun. Ekip arkadaşlarınızın rahatça anlayabileceği seviyede fonksiyonel yaklaşım uygulayın.
Bilgi: JavaScript tamamen fonksiyonel bir dil değildir; çoklu paradigma destekler. FP prensiplerini OOP ile birlikte kullanarak en iyi sonuçları elde edebilirsiniz. React'ın hooks sistemi, fonksiyonel programlama prensiplerinin pratik bir uygulamasıdır.

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.

Kaynaklar

İlgili Yazılar