REPL (read-eval-print loop) adalah mesin utama pengembangan pada Clojure: proses yang berjalan itu membaca satu form, mengevaluasinya, mencetak nilainya, lalu menunggu form berikutnya, dan putaran itu berulang sepanjang sesi. Topik ini menjelaskan cara REPL bekerja, menumbuhkan satu fungsi rata-rata dari eksperimen kecil melalui tiga langkah yang semuanya dijalankan sungguhan, lalu membandingkan ritme kerja ini dengan siklus kompilasi yang panjang. Siklus yang sama digunakan untuk setiap eksperimen pada jalur ini.
Apa yang dilakukan REPL#
REPL membaca satu form, mengevaluasinya di dalam proses yang sedang berjalan, mencetak nilai hasilnya, dan menunggu form berikutnya. Keempat tahap itulah yang dirangkum namanya, yaitu read, eval, print, dan loop. Prompt user=> menandakan form dievaluasi pada namespace user, namespace bawaan sesi yang baru dimulai. Sesi REPL dimulai dengan memanggil clojure tanpa argumen pada terminal, dan sesi diakhiri dengan Ctrl+D.
# tidak-dijalankan
# mulai REPL interaktif, sesi berakhir dengan Ctrl+D
clojureBlok di atas tidak dijalankan pada pembahasan ini karena REPL interaktif menunggu masukan dan tidak berakhir dengan sendirinya. Transkrip berikut memperlihatkan dua evaluasi pertama pada sesi yang baru dimulai, lengkap dengan prompt-nya.
user=> (+ 40 2)
42
user=> (str "halo, " "Clojure")
"halo, Clojure"
user=>Baris yang berawalan prompt adalah form yang diketik, sedangkan baris tanpa prompt adalah nilai yang dicetak REPL. String dicetak lengkap dengan tanda kutipnya karena nilai kembalian direpresentasikan sebagaimana tertulis di kode.
Siklus tulis, evaluasi, amati, perluas#
Bekerja dengan REPL berarti mengulang siklus empat langkah: menulis form, mengevaluasinya, mengamati nilai yang dicetak, lalu memperluas form yang sudah terbukti berjalan. Satuan perubahannya kecil, satu form saja, sehingga umpan balik datang seketika dan kesalahan ditemukan pada form yang tepat, bukan beberapa langkah kemudian. Tidak ada langkah build dan tidak ada proses baru yang harus dimulai di antara dua evaluasi, karena keadaan proses dipertahankan, termasuk dependensi yang sudah dimuat. Siklus ini juga mengubah cara pertanyaan kecil dijawab. Bentuk keluaran sebuah fungsi pustaka atau perilaku pembagian pecahan tidak perlu dicari lebih dahulu di dokumentasi, karena form kecil dapat diketik dan nilainya diamati langsung. Dokumentasi tetap digunakan, tetapi REPL menyelesaikan keraguan kecil dalam hitungan detik. Form yang sudah terbukti dipindahkan ke berkas sumber secara berkala, sedangkan definisi yang hanya hidup di REPL bersifat sementara dan hilang ketika sesinya berakhir. Contoh pada bagian berikut mengikuti siklus itu tiga kali: dimulai dari data literal dan perhitungan langsung, dilanjutkan dengan penamaan fungsi, dan diakhiri laporan yang menampilkan data per siswa.
Menumbuhkan fungsi rata-rata dalam tiga langkah#
Masalahnya sederhana: menghitung rata-rata nilai ujian lima siswa dengan skor [78 91 85 66 74]. Langkah pertama tidak mendefinisikan fungsi apa pun. Data literal langsung diolah dengan reduce dan count, lalu hasilnya diamati.
(def nilai-ujian [78 91 85 66 74])
(println "jumlah skor :" (reduce + nilai-ujian))
(println "banyak nilai:" (count nilai-ujian))
(println "rata-rata :" (/ (reduce + nilai-ujian) (count nilai-ujian)))jumlah skor : 394
banyak nilai: 5
rata-rata : 394/5Hasil 394/5 adalah temuan yang layak diamati: pembagian bilangan bulat pada Clojure menghasilkan Ratio, pecahan eksak, bukan pembulatan desimal. Pada REPL, temuan semacam ini terlihat pada evaluasi pertama, jauh sebelum kode ditata. Hasil amatan itu dibawa ke langkah kedua, yaitu perhitungan yang terbukti diberi nama sebagai fungsi rata-rata, dan rasionya dikonversi dengan double agar laporan menampilkan desimal.
(def nilai-ujian [78 91 85 66 74])
(defn rata-rata [xs]
(double (/ (reduce + xs) (count xs))))
(println "matematika:" (rata-rata nilai-ujian))
(println "bahasa :" (rata-rata [82 79 88 71]))matematika: 78.8
bahasa : 80.0Langkah ketiga memperluas fungsi menjadi laporan per siswa. Setiap siswa direpresentasikan sebagai map, dan parameter fungsi menggunakan pola {:keys [nama nilai]} untuk mengikat nilai keyword :nama dan :nilai ke nama yang lebih pendek. Teknik pengikatan bentuk data pada parameter ini disebut destructuring. Fungsi format menyusun teks dengan pola: %s menyalin string, %.1f menampilkan bilangan pecahan dengan satu digit desimal, dan %d menampilkan bilangan bulat.
(def nilai-ujian [78 91 85 66 74])
(defn rata-rata [xs]
(double (/ (reduce + xs) (count xs))))
(defn laporan-siswa [{:keys [nama nilai]}]
(format "%s: rata-rata %.1f dari %d nilai" nama (rata-rata nilai) (count nilai)))
(println (laporan-siswa {:nama "Ali" :nilai nilai-ujian}))
(println (laporan-siswa {:nama "Citra" :nilai [82 79 88 71]}))Ali: rata-rata 78.8 dari 5 nilai
Citra: rata-rata 80.0 dari 4 nilaiKetiga blok memuat ulang definisi yang diperlukannya agar masing-masing dapat dieksekusi sendiri-sendiri. Pada sesi REPL sungguhan, definisi tetap tersimpan di memori, sehingga langkah kedua dan ketiga tidak mengetik ulang baris yang sudah ada. Mengetik ulang itu murni kebutuhan penyajian di sini. Alur tiga langkah itulah inti REPL-first: form diuji, hasilnya diamati, lalu form yang terbukti diperluas menjadi bentuk berikutnya.
REPL dibandingkan siklus kompilasi panjang#
Perbedaan utama REPL dengan alur bahasa yang dikompilasi lebih dahulu terletak pada satuan perubahan dan jeda umpan baliknya. Tabel berikut merangkum perbedaan tersebut.
| Aspek | REPL | Siklus kompilasi panjang |
|---|---|---|
| Satuan perubahan | satu form | satu berkas atau lebih |
| Jeda umpan balik | seketika | menit atau lebih |
| Pengamatan hasil | nilai langsung tercetak | melalui log keluaran program |
| Keadaan proses | terpelihara antar-evaluasi | dibangun ulang pada tiap perubahan |
Keuntungan utamanya terletak pada eksplorasi. Data nyata dapat dimuat, diambil sebagian, dan diubah bentuknya sambil mengamati setiap langkah, termasuk mencoba pustaka yang baru ditambahkan tanpa menuliskan program pembungkus. Ritme ini juga mengubah cara kesalahan ditemukan, karena evaluasi yang gagal menghentikan satu form saja, sedangkan proses dan dependensi yang sudah dimuat tetap berjalan.
Risikonya juga perlu dikenali. Keadaan REPL adalah akumulasi seluruh definisi yang pernah dievaluasi, sehingga REPL dapat menyimpang dari berkas sumber bila definisi di berkas disunting tanpa dimuat ulang, atau bila definisi eksperimen tetap tertinggal di memori. Kebiasaan yang menjaga kesesuaian adalah menjadikan berkas sumber sebagai tempat penyimpanan tunggal dan memuat ulang berkas itu setelah setiap suntingan.
Kesalahan umum#
- Menuliskan definisi penting hanya di REPL. Definisi yang belum dipindahkan ke berkas sumber hilang ketika sesi berakhir, dan REPL yang baru dimulai tidak memuatnya lagi. Jadikan berkas sumber tempat penyimpanan tunggal, lalu pindahkan form yang sudah terbukti sebelum eksperimen berlanjut.
- Mengharapkan mode skrip mencetak nilai form. Skrip yang berisi
(* (+ 1 2) 4)tanpa println tidak mencetak apa pun, karena mode skrip tidak mencetak nilai kembalian. Bungkus hasil yang ingin dilihat dengan println atau prn. - Membaca dua baris keluaran REPL sebagai satu hasil. Mengevaluasi
(println "halo")di REPL memunculkanhalolalunil: baris pertama adalah keluaran dari println, sedangkanniladalah nilai kembalian form yang dicetak REPL. Keduanya berasal dari mekanisme yang berbeda. - Mengira REPL macet karena prompt tidak muncul kembali. Penyebab yang lazim adalah kurung yang belum tertutup, sehingga REPL masih menunggu sisa form dan menampilkan baris lanjutan alih-alih prompt. Tutup kurung yang kurang, dan bila strukturnya sudah kacau, mulai ulang sesi lalu muat ulang berkas sumber.
Ringkasan#
- REPL mengevaluasi satu form pada proses yang sedang berjalan dan mencetak nilainya, sehingga satuan perubahan terkecil adalah form.
- Fungsi tumbuh dari eksperimen: data literal dengan reduce, lalu fungsi bernama dengan double, lalu laporan berformat dengan destructuring.
- Pembagian bilangan bulat menghasilkan Ratio eksak seperti
394/5, dan double mengubahnya menjadi desimal. - REPL dapat menyimpang dari berkas sumber, sehingga berkas dimuat ulang setelah setiap suntingan.
Referensi#
- REPL and main — Clojure — Referensi resmi REPL, mode skrip, dan opsi -M yang digunakan untuk mengeksekusi berkas.
- Introduction to the REPL — Clojure — Panduan resmi penggunaan REPL untuk evaluasi form.
- Calva User Guide — Calva — Perintah jack-in dan perintah muat berkas pada Calva.