4 poin oleh GN⁺ 2025-08-20 | 1 komentar | Bagikan ke WhatsApp
  • Pendekatan pemrograman dari kiri ke kanan menjaga program tetap dalam keadaan valid segera setelah kode diketik, sehingga dukungan alat seperti pelengkapan otomatis di editor dapat dimaksimalkan
  • List comprehension Python menghambat pelengkapan otomatis karena variabel belum dideklarasikan dan tidak adanya inferensi tipe
  • Rust dan JavaScript memungkinkan program disusun secara alami dari kiri ke kanan, sehingga penggunaan variabel dan penelusuran metode terasa lebih intuitif
  • Gaya fungsional di C dan Python menghambat pengalaman menulis kode yang efisien karena nama fungsi atau strukturnya sulit ditemukan
  • Pada logika yang kompleks, kode yang berkembang dari kiri ke kanan lebih mudah dibaca, serta lebih baik dalam pemeliharaan dan skalabilitas

Pemrograman dari Kiri ke Kanan

Kode harus valid segera saat diketik


Batasan list comprehension Python

  • Sintaks list comprehension Python words_on_lines = [line.split() for line in text.splitlines()] menimbulkan masalah karena harus mengakses variabel yang belum dideklarasikan (line), sehingga editor tidak dapat memberikan pelengkapan otomatis atau inferensi tipe dengan baik
  • Dalam proses mengetik kode secara bertahap
    • jika mengetik words_on_lines = [line.sp, editor tidak mengetahui tipe line sehingga tidak bisa merekomendasikan metode
    • juga lebih sulit mendeteksi potensi kesalahan seperti salah ketik nama variabel (lime dan sebagainya)
  • Untuk mendapatkan rekomendasi yang benar, pengguna harus menulis kode yang belum selesai, dan proses itu terasa tidak intuitif dan tidak nyaman

Penyusunan dari kiri ke kanan di Rust

  • Contoh Rust (let words_on_lines = text.lines().map(|line| line.split_whitespace());) menunjukkan bahwa
    • bersamaan dengan deklarasi fungsi anonim, saat variabel (line) pertama kali muncul, itu langsung dianggap sebagai deklarasi, sehingga pelengkapan otomatis dan rekomendasi metode segera tersedia
    • bahkan metode split_whitespace juga mudah ditemukan berkat rekomendasi otomatis
  • Pendekatan ini membuat program selalu tetap dalam keadaan yang setidaknya valid secara parsial, sehingga IDE atau editor dapat mendukung proses menulis kode secara real-time

Progressive Disclosure dan kegunaan API

  • Progressive Disclosure adalah prinsip desain yang membuat pengguna hanya menghadapi kompleksitas sesuai kebutuhan, dan ini juga bisa diterapkan pada pemrograman
    • Contoh: mirip dengan UX pengolah kata yang hanya menampilkan opsi terkait saat menambahkan gambar
  • Bahasa C kurang memiliki dukungan seperti ini
    • karena semua fungsi yang terkait dengan FILE *file tidak dapat dijelajahi lewat file., pengguna harus menghafal pola nama fungsi (fread, fclose, dan sebagainya), sehingga sulit menemukan fitur
    • sebaliknya, dalam bahasa yang ideal, fungsi terkait bisa ditemukan secara bertahap dengan mudah melalui rekomendasi metode dari file.

Perbedaan dalam ketercarian fungsi dan metode

  • Perbandingan antara contoh Python map(len, text.split()) dan JavaScript text.split(" ").map(word => word.length)
    • di Python, nama fungsi seperti len, length, atau size tidak selalu dapat diperkirakan, sehingga harus mencoba beberapa kemungkinan untuk mengetahui mana yang benar-benar bekerja
    • di JavaScript, cukup mengetik .l setelah word. dan editor akan menampilkan metode seperti length, sehingga ketercarian lebih tinggi
    • fungsi tingkat tinggi seperti map juga membuat nilai hasil dan tipe data yang dikembalikan langsung tampak jelas

Keunggulan penulisan terstruktur pada logika yang kompleks

  • Untuk logika yang kompleks (kode Python panjang dengan filter dan lambda yang bertumpuk)
    • pengguna harus berulang kali memeriksa awal dan akhir kode, dan pencocokan kondisi maupun tanda kurung dapat menyebabkan penurunan keterbacaan serta kesulitan dalam memahami kode
  • Dalam versi JavaScript dengan logika yang sama, kode dapat dibaca dan dipahami secara berurutan dari atas ke bawah dan dari kiri ke kanan

Prinsip inti

Kode harus valid pada setiap saat ketika diketik

  • Bahkan saat hanya mengetik text, program tetap berada dalam keadaan valid
  • Setelah menulis text.split(" "), lalu melanjutkannya dengan .map(word => word.length), secara keseluruhan keadaan menengahnya selalu valid
  • Pola penulisan kode seperti ini meningkatkan kemungkinan dukungan real-time dari editor, dan dalam lingkungan REPL hasilnya juga bisa langsung diperiksa

Kesimpulan

  • Desain API dan bahasa harus mendukung pengetikan kode secara alami dari kiri ke kanan sambil memungkinkan terbentuknya program yang valid di setiap tahap perantara
  • Desain API yang baik adalah kunci untuk meningkatkan pengalaman menulis kode seperti ini

1 komentar

 
GN⁺ 2025-08-20
Komentar Hacker News
  • Salah satu kekurangan SQL adalah query dimulai dengan SELECT, bukan FROM, jadi sulit langsung memahami entitas (tabel) apa yang sedang ditangani, dan ini juga menghambat editor pintar untuk membantu penulisan query dengan lebih efisien; urutan FROM -> SELECT -> WHERE terasa lebih alami, terutama karena nama kolom ditentukan di klausa SELECT lalu dirujuk di WHERE, jadi menurut saya itu lebih masuk akal; bahkan saya rasa klausa SELECT bisa dihilangkan sehingga alih-alih SELECT * FROM table cukup menulis FROM table; keluhan seperti ini mungkin membuat saya terdengar seperti orang tua cerewet, tapi ini cuma kerinduan pribadi saya
    • PSQL dan PRQL memang memakai urutan query dengan FROM lebih dulu; BigQuery juga baru-baru ini menambahkan sintaks pipe/panah, dan ada extension komunitas DuckDB jadi layak direkomendasikan: DuckDB - PSQL, DuckDB - PRQL
    • Alasan SQL ditulis seperti ini adalah karena dalam dasar aljabar relasional, projection selalu ditulis lebih dulu; karena itu menurut standar, alias kolom tidak bisa dipakai di WHERE, sebab selection (WHERE) terjadi sebelum projection (SELECT); sebagai referensi, MySQL 8 juga punya sintaks TABLE <table> yang mungkin menarik
    • Sebenarnya urutan pemrosesan internal pada sebagian besar engine SQL adalah FROM -> WHERE -> SELECT, jadi alias kolom yang didefinisikan di SELECT bisa dipakai di GROUP BY, HAVING, ORDER BY, tetapi tidak di WHERE
    • Di C#, DSL yang dikompilasi ke SQL (LINQ-to-SQL) juga punya struktur dengan FROM lebih dulu, dan berkat autocomplete di IDE saat menulis klausa lain, kita bisa langsung mendapat saran field, jadi menurut saya struktur seperti ini bagus
    • Kusto, bahasa query analisis data milik Azure, juga berbentuk mirip dengan pipe Pengantar query Kusto, demikian pula gaya LINQ di .NET; sejujurnya saya rasa varian SQL yang dimulai dari FROM perlu diadopsi lebih agresif, dan itu juga bukan hal yang sulit; menurut saya upaya untuk meningkatkan kegunaan masih kurang
  • Saya sulit memahami kenapa Python begitu dicintai; begitu dikerjakan oleh lebih dari dua orang, bahasanya terasa sangat menyiksa tanpa batas; poin yang ditunjukkan penulis hanyalah puncak gunung es
    • Menurut saya alasannya mirip dengan kenapa orang tidak berbondong-bondong ke bahasa keluarga Lisp; ketelitian matematis tidak otomatis berarti mudah dibaca; list/dict/set comprehension di Python sama seperti loop for yang tipenya sudah ditentukan; semua orang khawatir tipe Python terlalu longgar, tetapi aneh kalau satu-satunya sintaks yang dengan jelas menentukan tipe hasil, yaitu list comprehension, justru dijadikan sasaran; di sebagian besar bahasa lain termasuk Rust pun urutannya bukan "from iter as var"; menarik juga membandingkan sintaks pemanggilan fungsi antarbahasa (sama seperti Python juga punya functools.map)
    • Menurut saya, hanya karena seseorang tidak memahami sesuatu bukan berarti itu sebuah keutamaan; jelas ada alasan mengapa Python disukai; tentu kekurangannya juga nyata, tetapi itu saja tidak cukup berarti; kelebihan dan kekurangan harus dibandingkan secara menyeluruh, dan begitu juga jika dibandingkan dengan bahasa lain
    • Saya juga suka Python (tetapi untuk tim kecil dan program pendek yang umurnya singkat); karena tidak ada static typing, implementasinya cepat, tetapi berkat sistem tipe yang cukup kuat, semuanya tidak sampai benar-benar berantakan; menurut saya inilah alasan Python populer di data science; sangat nyaman untuk eksplorasi; sebaliknya, untuk pemeliharaan jangka panjang oleh banyak tim atau program besar, jelas ada kekurangannya; pada akhirnya tidak ada bahasa serbabisa, dan setidaknya kita butuh dua jenis: "bahasa yang enak untuk mencoba dan melaju cepat (Soft)" dan "bahasa yang baik untuk dikelola dalam jangka panjang (Hard)"
    • Dulu saya sepenuhnya setuju dengan pendapat di atas, tetapi berkat type annotation dan type checking, kolaborasi dengan kode Python yang ditulis orang lain jadi jauh lebih mudah; saya masih tidak menganggapnya cocok sampai proyek besar, tetapi setelah ada tipe, Python menjadi bahasa skrip favorit saya
    • Di codebase bersama saya juga menghindari hal seperti list comprehension dan memilih gaya Python yang sangat sederhana; ini dikenal sebagai bahasa yang "harus punya satu cara saja", tetapi pada praktiknya terlalu banyak cara hidup berdampingan; list comprehension secara pribadi terasa menyenangkan dan memuaskan, tetapi kalau semua orang harus berjalan di satu jalur, menurut saya sintaks ini seharusnya tidak ada
  • Saya setuju dengan klaim bahwa "program seharusnya valid pada saat sedang diketik", tetapi di dunia nyata kita tidak selalu menulis kode dari kiri ke kanan, satu baris demi satu baris secara berurutan; sering kali kita menulis bagian lain lebih dulu atau mendeklarasikan variabel belakangan; misalnya, ada kalanya kita memakai variabel dulu lalu baru mendeklarasikannya jauh setelah itu
    • Mengingat kode ditulis sekali lalu dibaca puluhan atau ratusan kali, menurut saya kode yang bisa dibaca secara berurutan jauh lebih nyaman dibaca daripada kode yang mengharuskan lompatan
    • Sebenarnya diskusi ini agak menyimpang dari inti tulisan, tetapi ini sudut pandang yang menarik
    • Sangat setuju; hanya saat membuat file baru saya menulis kode secara berurutan dari awal; saat menambah field, saya tidak perlu mulai dari definisi kelas, melainkan langsung menulis kode yang memakai field itu; saat memperbaiki conditional pun sering kali untuk sementara kode berada dalam keadaan tidak valid (error)
    • Saya juga setuju dengan pendapat ini, tetapi prinsip penting yang terkait dengannya adalah bahwa struktur yang "bahkan mencegah Anda melakukan kompilasi karena Anda belum selesai menulis kode" itu berlebihan; error seharusnya non-blocking, tetapi beberapa bahasa justru memblokir kode yang belum selesai sama sekali (misalnya variabel tak terpakai, return yang hilang, dll.)
    • Kadang terasa sedikit tidak nyaman karena IDE seolah tidak memahami urutan sebenarnya saat saya menulis kode
  • Di beberapa IDE ada fitur template kode yang memungkinkan kita mengetik singkatan lalu memperluasnya menjadi struktur kode, dan mengisi tiap placeholder dengan tombol tab; pada saat itu urutan perpindahan tab tidak harus dari kiri ke kanan, jadi misalnya urutan seperti {3} for {2} in {1} juga mungkin; alat seperti ini memberi titik kompromi antara "sintaks yang enak dibaca" dan "sintaks yang enak diketik"; saya pribadi cenderung mendukung sintaks yang enak dibaca, bahkan kalau harus dibantu tooling; menurut saya tidak perlu ngotot hanya pada struktur "for-in"
  • Belakangan ini suasana konsensus di Hacker News tampaknya adalah bahwa Python melewatkan operator pipe; saya cepat menyadari nilai pipe saat berpindah dari Mathematica ke R; saat menulis kode transformasi data bertahap di data science, ini benar-benar intuitif dan mudah dibaca; Python memang dipakai di banyak bidang, tetapi saya penasaran apakah pipe juga punya manfaat di konteks selain analisis data; saya ingin memahami kenapa Python tidak mengadopsi pipe
    • Selangkah lebih jauh dari operator pipe, saya rasa reverse assignment juga layak dicoba; alih-alih mengalokasikan hasil ke variabel seperti 'let foo = ...', saya ingin mencoba bentuk seperti '... =: foo'
    • Operator pipe di R (terutama tidyverse R) adalah "killer app" terpenting bagi saya; saya rasa tidak ada bahasa lain yang membuat pekerjaan data semudah dan senyaman ini; misalnya alih-alih menumpuk resep kue seperti bake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12), memakai pipe seperti mix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12) jauh lebih mudah dan enak dilihat
    • Jika memakai sintaks pipe di pandas Python, jadinya seperti di bawah ini
      result = (df
       .pipe(fun1, arg1=1)
       .pipe(fun2, arg2=2)
      )
      
      Di R,
      result <- df |>
       fun1(., arg1=1) |>
       fun2(., arg2=2)
      
      Keduanya cukup enak dibaca, tetapi kelebihan R adalah pipe bekerja lebih baik juga di luar dataframe
  • Perdebatan ini hampir seperti perang agama, mirip perdebatan bahasa FP (fungsional) vs OOP (berorientasi objek), atau vim vs emacs; di vim operator datang lebih dulu, sedangkan di emacs urutan pemilihan datang lebih dulu; bahasa yang "terbaca seperti bahasa Inggris" biasanya punya struktur verba di depan (seperti Lisp/Scheme), sedangkan bahasa seperti Jerman atau Tamil yang meletakkan verba di belakang lebih cocok dengan gaya OOP (nomina lebih dulu); misalnya dalam bahasa Tamil urutannya "water drink", sedangkan dalam bahasa Inggris "drink water"; jadi bisa saja ada orang yang merasa vim lebih nyaman; bukannya salah satu gaya selalu lebih baik, tetapi kadang alat dibuat agar cocok dengan kecenderungan orang tertentu, dan sekarang dengan model bahasa saya rasa hampir semua hal bisa dilakukan
    • Mengenai "kalau dirancang agar terbaca seperti bahasa Inggris, apakah verba harus duluan", untuk bahasa imperatif iya, tetapi dalam bahasa deklaratif, jika mengikuti bahasa Inggris, subjek justru datang lebih dulu
    • Soal "apakah dalam bahasa Jerman verba selalu di belakang", sebenarnya dalam kalimat sederhana verba berada di posisi kedua ("I drink water" → "Ich trinke Wasser"), jadi tidak selalu benar-benar di akhir kalimat
    • Terkait pernyataan bahwa di vim operator lebih dulu, sebenarnya Kakoune bekerja sebaliknya, dan menurut saya cara ini jauh lebih logis Penjelasan tentang Kakoune
  • Di sisi lain, sintaks Python seperti "from some_library import child_module" sangat intuitif; di JS, strukturnya seperti "import { asYetUnknownModule } from SomeLibrary" sehingga terasa jauh kurang intuitif
    • Di JS, kalau memakai namespace import seperti berikut
      import * as someLibrary from "some-library"
      someLibrary.someFunction()
      
      sebenarnya autocomplete IDE bekerja dengan baik, jadi menurut saya ini bagus Penjelasan MDN tentang namespace import
    • Saya mempertanyakan obsesi pada keyword "from"; menurut saya cukup dibuat seperti
      import SomeLibrary {
        asYetUnknownModule
      }
      
      begitu saja
  • ReScript mengubah API justru karena alasan ini, dari data-last menjadi data-first; berkat type inference yang sangat baik, hampir selalu tersedia autocomplete yang akurat dan sesuai tipe, sehingga pengalaman pengembang sangat bagus; tentu kalau mendeklarasikan fungsi tanpa referensi apa pun (karena tipenya tidak diketahui) masalahnya tetap ada, tetapi itu bisa diatasi dengan menambahkan tipe atau memanggilnya lebih dulu; saya juga merekomendasikan tulisan blog terkait Perbandingan data-first dan data-last
  • Saya sudah lama terus mengemukakan sudut pandang ini, dan ini juga berkaitan dengan alasan Ruby terasa jauh lebih mudah bagi saya; khususnya, saya belum pernah memakai Python maupun Ruby secara mendalam pada level production, tetapi saya tetap sulit memahami kenapa Python bisa mulai terpasang dan dipakai seluas itu; Ruby juga bukan tanpa kekurangan, tetapi tampaknya tidak banyak orang yang dalam penulisan skrip harus menghadapi perubahan rumit seperti yang dimiliki Python; setidaknya Ruby tidak mengalami insiden benturan versi besar dalam 10 tahun terakhir
  • Secara keseluruhan saya sepenuhnya setuju dengan poin-poin yang ditunjukkan artikel; saya rasa struktur di mana konteks datang lebih dulu dan dibaca dari kiri ke kanan akan lebih cocok untuk LLM atau autocomplete; namun, daripada menulis kode contoh seperti 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))), lebih baik memanfaatkan NumPy array karena tidak perlu membuat list baru di memori dan juga jauh lebih baik untuk menangani seluruh baris sekaligus, misalnya
    sum(1 for line in diffs
      if ((np.abs(line) >= 1) & (np.abs(line) <= 3)).all()
        and ((line > 0).all() or (line < 0).all()))
    
    yang ini jauh lebih mencerminkan "dari kiri ke kanan"
    • Versi numpy ini juga masih agak terasa seperti kode sandi ("line > 0" masih oke, tetapi aturan broadcasting bisa menjadi rumit), dan API koleksi di bahasa dengan tipe yang ketat seperti contoh JavaScript, C#, Java, atau Scala yang dibawa penulis terasa lebih rapi; selera saya pribadi adalah Kotlin, karena saya suka bisa menulis seperti berikut
      diffs.countIf { line -> 
        line.all { abs(it) in 1..3 } and ( 
          line.all { it > 0} or
          line.all { it < 0}
        )
      }