4 puan yazan GN⁺ 2025-08-20 | 1 yorum | WhatsApp'ta paylaş
  • Soldan sağa programlama yaklaşımında, kod yazılır yazılmaz program geçerli durumda kalır; bu da editörün otomatik tamamlama gibi araç desteğini en üst düzeye çıkarır
  • Python’un liste üreteçleri, bildirilmeyen değişkenler ve tip çıkarımının yokluğu nedeniyle otomatik tamamlama işlevini engeller
  • Rust ve JavaScript, programın soldan sağa doğal biçimde kurulmasına izin verdiği için değişken kullanımı ve metot keşfi daha sezgiseldir
  • C ve Python’daki fonksiyonel stil, fonksiyon adı ya da yapının kolay keşfedilememesi nedeniyle verimli bir kodlama deneyimini zayıflatır
  • Karmaşıklığı yüksek mantıkta, soldan sağa açılan kod daha kolay okunur; bakım ve genişletilebilirlik açısından daha üstündür

Soldan Sağa Programlama

Kod, yazıldığı anda geçerli olmalı


Python liste üreteçlerinin sınırları

  • Python liste üreteci sözdizimi words_on_lines = [line.split() for line in text.splitlines()], bildirilmeyen bir değişkene (line) erişmeyi gerektirdiği için editörün otomatik tamamlama ya da tip çıkarımı desteğini düzgün sağlayamaması sorununu doğurur
  • Kodu parça parça yazma sürecinde
    • words_on_lines = [line.sp gibi yazıldığında editör line değişkeninin tipini bilemediğinden metot öneremez
    • Değişken adı yazım hataları (lime gibi) gibi potansiyel hataları tespit etmek de zorlaşır
  • Doğru önerileri alabilmek için tamamlanmamış kod yazmak gerekir; bu süreç sezgisel değildir ve rahatsız edicidir

Rust’ta soldan sağa kurulum

  • Rust örneği (let words_on_lines = text.lines().map(|line| line.split_whitespace());) şunu gösterir:
    • Anonim fonksiyon bildirilirken değişken (line) ilk göründüğü anda bildirilmiş sayılır; böylece hemen otomatik tamamlama ve metot önerileri kullanılabilir
    • Nitekim split_whitespace metodu da otomatik öneri sayesinde kolayca bulunabilmiştir
  • Bu yaklaşımda program her zaman kısmen de olsa geçerli durumda kaldığı için IDE ya da editör kod yazımını gerçek zamanlı destekleyebilir

Aşamalı Açığa Çıkarma (Progressive Disclosure) ve API kullanılabilirliği

  • Aşamalı Açığa Çıkarma (Progressive Disclosure), kullanıcının yalnızca ihtiyaç duyduğu kadar karmaşıklıkla karşılaşmasını sağlayan bir tasarım ilkesidir; programlamaya da uygulanabilir
    • Örnek: Bir kelime işlemcide yalnızca görsel eklendiğinde ilgili seçeneklerin görünmesiyle benzerlik taşır
  • C dili bu tür desteği yeterince sunmaz
    • FILE *file ile ilişkili tüm fonksiyonlar file. üzerinden keşfedilemediği için fread, fclose gibi fonksiyon adı kalıplarını ezberlemek gerekir ve işlevleri keşfetmek zordur
    • Buna karşılık ideal bir dilde file. üzerinden metot önerileriyle ilgili işlevler kolayca aşamalı olarak keşfedilebilir

Fonksiyon ve metot keşfedilebilirliği arasındaki fark

  • Python’daki map(len, text.split()) ile JavaScript’teki text.split(" ").map(word => word.length) örneklerinin karşılaştırılması
    • Python’da len, length, size gibi isimler öngörülemediğinden doğru işlevi bulmak için birkaç deneme yapmak gerekebilir
    • JavaScript’te word. sonrasına yalnızca .l yazmak bile editörün length gibi metotları önermesini sağlar; bu da keşfedilebilirliği artırır
    • map gibi yüksek mertebeden fonksiyonlarda bile gerçek dönüş değeri ve veri tipi anında daha açık hale gelir

Mantık karmaşıklaştıkça yapısal yazımın avantajı

  • Karmaşıklığı yüksek mantıkta (filter, lambda iç içe geçmiş uzun Python kodu gibi)
    • Kodun başını ve sonunu tekrar tekrar kontrol etmek gerekir; koşul ifadeleri ya da parantez eşleştirmeleri nedeniyle okunabilirlik düşer ve anlamak zorlaşır
  • Aynı mantığın JavaScript sürümünde ise kod yukarıdan aşağıya, soldan sağa sıralı biçimde okunup anlaşılabilir

Temel ilke

Kod, girildiği her anda geçerli olmalı

  • Tek başına text yazmak bile programın geçerli durumda kalmasını sağlar
  • text.split(" ") yazıldığında da, ardından .map(word => word.length) eklenirken de, bütün olarak ara durumlar her zaman geçerlidir
  • Bu tür bir kodlama deseni, editörün gerçek zamanlı destek sunabilmesini artırır; REPL ortamında ise sonuçlar anında görülebilir

Sonuç

  • API ve dil tasarımı, kodun soldan sağa doğal biçimde yazılmasını ve her ara adımda geçerli bir program oluşturulmasını desteklemelidir
  • İyi API tasarımı, bu kod yazma deneyimini iyileştirmenin temel unsurudur

1 yorum

 
GN⁺ 2025-08-20
Hacker News görüşleri
  • SQL'in dezavantajlarından biri, sorgunun FROM yerine SELECT ile başlaması; bu yüzden hangi varlıkla (tabloyla) çalıştığını hemen anlamak zor oluyor ve akıllı editörlerin sorgu yazımını daha verimli desteklemesini de engelliyor. FROM -> SELECT -> WHERE sırası daha doğal geliyor; özellikle de SELECT bölümünde sütun adları belirlenip WHERE içinde bunlara başvurulduğu için. Hatta SELECT * FROM table yerine yalnızca FROM table yazıldığında SELECT bölümünün atlanabilmesi gerektiğini düşünüyorum. Bu şikayetler yüzünden dırdırcı bir ihtiyar gibi görünebilirim ama bu sadece kişisel bir özlemim.
    • PSQL ve PRQL gerçekten de FROM'un önce geldiği sorgu sırasını kullanıyor. BigQuery'ye de yakın zamanda pipe/ok sözdizimi eklendi, DuckDB için de topluluk eklentileri var; tavsiye ederim: DuckDB - PSQL, DuckDB - PRQL
    • SQL'in böyle yazılmasının nedeni, ilişkisel cebirin temelinde projection'ın her zaman önce yazılması. Bu yüzden standarda göre WHERE içinde sütun takma adı kullanılamaz; çünkü selection (WHERE), projection'dan (SELECT) önce gerçekleşir. Ayrıca MySQL 8'de TABLE <table> diye bir sözdizimi de var, buna da bakılabilir.
    • Aslında çoğu SQL motorunun iç işleme sırası FROM -> WHERE -> SELECT şeklindedir. Bu yüzden SELECT içinde tanımlanan sütun takma adları GROUP BY, HAVING, ORDER BY içinde kullanılabilirken WHERE içinde kullanılamaz.
    • C# tarafında SQL'e derlenen DSL'ler de (LINQ-to-SQL) FROM ile başlayan bir yapı kullanıyor. Ayrıca IDE'de diğer bölümleri yazarken otomatik tamamlama sayesinde alan önerilerini hemen almak mümkün olduğu için bu yapıyı iyi buluyorum.
    • Azure'un veri analizi sorgu dili Kusto da pipe kullanan benzer bir yapıya sahip: Kusto sorgularına giriş. .NET'in LINQ stili de aynı şekilde. Açıkçası SQL'e FROM ile başlayan varyantların daha aktif biçimde eklenmesi gerektiğini düşünüyorum ve bunun da zor bir iş olduğunu sanmıyorum. Kullanılabilirliği artırmaya dönük denemeler yetersiz kalıyor.
  • Python'un neden bu kadar sevildiğini anlamakta zorlanıyorum; iki kişiden fazlası birlikte çalışınca dil sonsuz bir eziyete dönüşüyor. Yazarın işaret ettiği kısım buzdağının sadece görünen ucu.
    • Bence bu, insanların Lisp ailesi dillere üşüşmemesinin sebebiyle benzer. Matematiksel katılık, okunabilirlik demek değildir. Python'un list/dict/set comprehension yapıları, türü belirli olan for döngüsüyle aynı şeydir. Herkes Python'un tür sisteminin gevşekliğinden şikayet ederken, dönüş türünü açıkça belirleyen tek sözdiziminin (liste comprehension) hedef alınması garip. Rust dahil çoğu başka dilde de sıra from iter as var şeklinde değil. Dillerin fonksiyon çağırma sözdizimlerini karşılaştırmak da ilginç olabilir (functools.map Python'da da var sonuçta).
    • Bir şeyi anlayamıyor olmak onu erdem yapmaz. Python'un sevilmesinin mutlaka bir nedeni var. Elbette eksileri de açık, ama bunu tek başına söylemenin anlamı yok. Artı ve eksileri bütün olarak, başka dillerle kıyaslayarak değerlendirmek gerekir.
    • Ben de Python'u seviyorum (ama küçük ekipler, kısa ve kısa ömürlü programlar için). Statik tiplerin olmayışı uygulamayı hızlı geliştirmenizi sağlıyor ama güçlü tip sistemi sayesinde tamamen raydan da çıkmıyor. Veri biliminde popüler olmasının sebebi de bu bence; keşif yaparken çok rahat. Öte yandan uzun vadede, birden fazla ekibin bakım yaptığı veya büyük ölçekli programlarda belirgin dezavantajları var. Sonuçta tek bir evrensel dil yok; en azından "deneyip hızlı ilerlemeye uygun dil (Soft)" ile "uzun süre yönetmeye uygun dil (Hard)" diye iki türe ihtiyaç var.
    • Eskiden ben de bu görüşe tamamen katılıyordum ama tür ek açıklamaları ve tür denetimi sayesinde başkalarının yazdığı Python koduyla işbirliği yapmak çok daha kolaylaştı. Hâlâ çok büyük projeler için uygun olduğunu düşünmüyorum ama tipler eklendikten sonra Python benim en sevdiğim betik dili oldu.
    • Ben de ortak kod tabanında liste comprehension gibi yapıları kullanmamaya ve çok sade bir Python tarzı benimsemeye çalışıyorum. "Tek bir doğru yol vardır" diye bilinen bir dil ama gerçekte fazlasıyla çok yol bir arada var. Liste comprehension bana kişisel olarak eğlenceli ve tatmin edici geliyor ama herkesin tek bir yoldan gitmesi gerekiyorsa bu sözdizimi olmamalı.
  • "Program, yazıldığı anda geçerli olmalı" iddiasına sempati duyuyorum ama gerçekte kodu her zaman soldan sağa, satır satır, sırayla yazmıyoruz. Bazen ortada başka bir kısmı önce yazıyor ya da değişken bildirimini sona bırakıyoruz. Mesela bir değişkeni kullanıp onu epey sonra tanımladığımız da oluyor.
    • Kod bir kez yazılıp onlarca, yüzlerce kez okunduğu için, sıralı okunabilen kodun atlama gerektiren koda göre çok daha rahat okunabildiğini düşünüyorum.
    • Aslında bu tartışma yazının ana fikrinden biraz sapıyor ama ilginç bir bakış açısı.
    • Kesinlikle katılıyorum. Yalnızca yeni bir dosya oluştururken kodu en baştan sırasıyla yazıyorum. Bir alan eklerken ille de sınıf tanımına gitmek yerine önce doğrudan o alanı kullanan kodu yazarım. Bir koşulu iyileştirirken de bir süreliğine geçersiz (hatalı) durumda kalmak çok olağan.
    • Buna da katılıyorum ama bununla bağlantılı önemli bir ilke şu: "Daha kodu bitirmedin diye derlemeyi tamamen engelleyen" yapı fazla sert. Hatalar engelleyici olmamalı; bazı diller tamamlanmamış kodu tamamen bloke ediyor (ör. kullanılmayan değişken, eksik return vb.).
    • Bazen IDE'nin benim kodu gerçekte hangi sırayla yazdığımı iyi anlamadığını hissediyorum ve bu biraz rahatsız edici.
  • Bazı IDE'lerde kod şablonlarıyla kısaltma yazınca bir kod yapısına genişletme ve ardından Tab ile yer tutucuları tek tek doldurma özelliği var. Bu durumda Tab ile ilerleme sırasının mutlaka soldan sağa olması gerekmez; örneğin {3} for {2} in {1} gibi bir sıra da mümkün. Bu tür araçlar, "okuması kolay sözdizimi" ile "yazması kolay sözdizimi" arasında bir uzlaşma sunuyor. Ben, araçlardan yararlanmak gerekse bile okunabilir sözdizimini önceleyen taraftayım. Mutlaka for-in yapısına saplanmak gerekmiyor.
  • Son zamanlarda Hacker News'te oluşan ortak hava, Python'un pipe operatörünü kaçırmış olduğu yönünde. Ben Mathematica'dan R'ye geçerken pipe'ın değerini çok hızlı fark etmiştim. Veri biliminde adım adım veri dönüştürme kodu yazarken gerçekten sezgisel ve okunması kolay. Python pek çok alanda kullanılıyor ama veri analizi dışındaki bağlamlarda da pipe'ın avantajı olur mu merak ediyorum. Python'un neden bunu eklemediğini anlamaya çalışıyorum.
    • Pipe operatöründen bir adım ileri gidip reverse assignment da denenebilir diye düşünüyorum. 'let foo = ...' gibi sonucu değişkene atamak yerine '... =: foo' gibi bir biçimi denemek ilginç olurdu.
    • R'nin (özellikle tidyverse R'nin) pipe operatörü benim için en önemli "killer app". Veriyle çalışmayı bu kadar kolay ve keyifli hâle getiren başka bir dil yok bence. Mesela bir kurabiye tarifini bake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12) gibi iç içe yazmak yerine, mix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12) şeklinde pipe ile yazmak çok daha kolay ve anlaşılır.
    • Python pandas'ta pipe sözdizimi kullanılırsa şöyle oluyor:
      result = (df
       .pipe(fun1, arg1=1)
       .pipe(fun2, arg2=2)
      )
      
      R'de ise:
      result <- df |>
       fun1(., arg1=1) |>
       fun2(., arg2=2)
      
      İkisi de okunabilir ama R'nin avantajı, pipe'ın data frame dışındaki yerlerde de daha iyi çalışması.
  • Bu tartışma, FP (fonksiyonel) ve OOP (nesne yönelimli) diller arasındaki tartışma ya da vim ile emacs tartışması gibi neredeyse din savaşına benziyor. Vim'de operatör önce gelir, emacs'te ise seçim sırası önce gelir. İngilizce gibi "İngilizce okunuyormuş gibi" tasarlanan dillerde genelde fiil önce gelir (Lisp/Scheme gibi). Buna karşılık Almanca, Tamilce gibi fiilin sonda geldiği diller, OOP tarzıyla (önce isim/nesne) daha uyumlu olabilir. Örneğin Tamilcede sıra "water drink", İngilizcede ise "drink water". Bu yüzden vim'i daha doğal bulan insanlar olabilir. Burada bir stilin diğerinden üstün olmasından çok, araçların ve insanların eğilimlerine göre şekillenme meselesi var; bugünlerde dil modelleriyle zaten çoğu şey mümkün.
    • "İngilizce gibi okunacaksa fiil mi önce gelmeli?" sorusuna: Zorunlu olarak değil. Buyurgan dillerde evet ama bildirime dayalı dillerde İngilizce gibi okunacaksa özne önce gelir.
    • "Almancada fiil hep sonda mı gelir?" sorusuna: Aslında basit cümlelerde fiil ikinci sırada gelir ("I drink water" → "Ich trinke Wasser"); tamamen cümle sonunda olmak zorunda değildir.
    • Vim'de operatörün önce geldiği söylenmiş ama aslında Kakoune bunun tersine çalışıyor ve bence bu çok daha mantıklı: Kakoune açıklaması
  • Buna karşılık Python'un from some_library import child_module sözdizimi oldukça sezgisel. JS'de ise import { asYetUnknownModule } from SomeLibrary yapısı bana çok daha az sezgisel geliyor.
    • JS'de namespace import şu şekilde kullanılırsa
      import * as someLibrary from "some-library"
      someLibrary.someFunction()
      
      IDE otomatik tamamlama gerçekten iyi çalışıyor; bu yüzden bence gayet kullanışlı: MDN namespace import açıklaması
    • Neden from anahtar sözcüğüne bu kadar takıldığınızı anlamıyorum. Basitçe
      import SomeLibrary {
        asYetUnknownModule
      }
      
      gibi bir şey de olabilirdi.
  • ReScript tam da bu nedenle API'lerini data-last'ten data-first'e çevirdi. Harika tür çıkarımı sayesinde neredeyse her zaman doğru ve tipe uygun otomatik tamamlama geliyor; bu da geliştirici deneyimini çok iyileştiriyor. Elbette bir fonksiyonu referans olmadan tanımlarsanız (tip bilinmeyeceği için) sorun yaşanıyor, ama tip ekleyerek ya da önce çağırarak bu çözülebiliyor. Şu blog yazısı da tavsiye edilir: Data-first ve data-last karşılaştırması
  • Uzun süredir bu bakış açısını savunuyorum ve bu, Ruby'nin bana neden çok daha kolay geldiğiyle de bağlantılı. Özellikle şunu söyleyeyim: Python'u da Ruby'yi de üretim düzeyinde derinlemesine kullanmış değilim ama Python'un neden bu kadar yaygın kurulduğunu ve kullanılmaya başlandığını anlamakta zorlanıyorum. Ruby'nin de kusurları var ama betik yazarken insanların Python'daki kadar karmaşık değişimlerle karşılaştığını sanmıyorum. En azından Ruby son 10 yılda büyük sürüm çakışmaları yaşamadı.
  • Genel olarak yazıda işaret edilen noktalara tamamen katılıyorum. Bağlamın önce gelmesi ve soldan sağa okunan yapıların LLM'lere ya da otomatik tamamlamaya daha iyi uyacağını düşünüyorum. Yine de örnek kodu len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) gibi yazmak yerine NumPy array kullanmak, bellekte yeni bir liste oluşturmaya gerek bırakmaz ve tüm satırı bir seferde ele almak açısından çok daha iyidir. Örneğin
    sum(1 for line in diffs
      if ((np.abs(line) >= 1) & (np.abs(line) <= 3)).all()
        and ((line > 0).all() or (line < 0).all()))
    
    bence "soldan sağa" fikrini çok daha iyi yansıtıyor.
    • NumPy sürümü de hâlâ biraz büyü gibi görünüyor ("line > 0" tamam ama broadcasting kuralları karmaşıklaşabiliyor). Yazarın verdiği JavaScript örneği ya da C#, Java, Scala gibi daha katı tiplere sahip dillerin koleksiyon API'leri daha temiz. Benim tercihim Kotlin; çünkü şöyle yazılabiliyor:
      diffs.countIf { line -> 
        line.all { abs(it) in 1..3 } and ( 
          line.all { it > 0} or
          line.all { it < 0}
        )
      }