Pemrograman · Bahasa pemrograman
Tutorial Clojure untuk pemula
Jawaban singkat
Clojure adalah Lisp dinamis dan fungsional yang berjalan di Java Virtual Machine (dengan ClojureScript untuk JavaScript). Kode ditulis sebagai list dalam tanda kurung, data bersifat immutable secara default, dan program dibangun dengan menggabungkan fungsi-fungsi kecil atas vector, map, dan set. Anda memasang Clojure CLI, memulai REPL dengan clj, dan menjalankan file dengan clojure -M hello.clj.
Apa itu Clojure dan siapa yang memakainya
Clojure dibuat oleh Rich Hickey dan pertama kali dirilis pada 2007. Clojure adalah dialek Lisp: setiap ekspresi adalah list yang elemen pertamanya berupa fungsi atau operator, misalnya (+ 1 2). Clojure dikompilasi menjadi bytecode JVM, sehingga dapat memanggil library Java apa pun secara langsung. ClojureScript mengompilasi bahasa yang sama ke JavaScript untuk browser dan Node.js.
Clojure dipakai untuk layanan web back-end, data processing pipeline, dan sistem bisnis internal, terutama oleh tim yang menghargai data immutable dan pengembangan interaktif. Developer biasanya bekerja di dalam REPL (read-eval-print loop) yang terhubung ke editor, mengevaluasi satu ekspresi sekali waktu sementara program berjalan.
Memasang Clojure dan menjalankan Hello, world
Pasang Java runtime (JDK 17 atau lebih baru adalah pilihan aman), lalu pasang Clojure CLI tools dari clojure.org (Homebrew di macOS, script instalasi di Linux, atau installer Windows). CLI menyediakan dua perintah: clj (REPL dengan penyuntingan baris) dan clojure (untuk menjalankan program).
;; hello.clj
(println "Hello, world!")clojure -M hello.clj
# Hello, world!
clj
# user=> (+ 1 2)
# 3Proyek nyata menyimpan dependensinya dalam file deps.edn dan kodenya di bawah src/, dengan satu namespace per file. Leiningen (lein) adalah build tool yang lebih lama tetapi masih populer, dengan file project.clj.
Nilai, tipe, dan nama
Clojure bertipe dinamis. def mengikat nama global (sebuah var) dan let mengikat nama lokal. Nilai tidak berubah setelah dibuat: tidak ada statement assignment.
| Tipe | Contoh | Catatan |
|---|---|---|
| Integer | 42 | long 64-bit; 42N adalah BigInt |
| Floating point | 3.14 | double Java |
| Ratio | (/ 1 3) | Pecahan eksak 1/3, bukan 0.333 |
| String | "hello" | String Java, hanya dengan tanda kutip ganda |
| Karakter | \a | Satu karakter |
| Keyword | :name | Label yang cepat dan mengevaluasi dirinya sendiri, sering dipakai sebagai key map |
| Boolean / nil | true, false, nil | Hanya false dan nil yang falsy |
(def pi-approx 3.14159)
(let [r 2
area (* pi-approx r r)]
(println "Area:" area))
;; Area: 12.56636
(str "a" 1 :b) ;; => "a1:b"Alur kontrol
Di Clojure segalanya adalah ekspresi yang mengembalikan nilai, termasuk if. if menerima tepat satu form "then" dan satu form "else"; gunakan when untuk beberapa efek samping tanpa cabang else, dan cond untuk beberapa kondisi.
(defn grade [score]
(cond
(>= score 90) "A"
(>= score 80) "B"
:else "C"))
(println (grade 85)) ;; B
(when (pos? 5) (println "positive") :done)
(doseq [x [1 2 3]] (println (* x x))) ;; 1 4 9
(for [x (range 5) :when (odd? x)] (* x 10)) ;; => (10 30)
(loop [i 0 acc 0]
(if (< i 5) (recur (inc i) (+ acc i)) acc)) ;; => 10Tidak ada perulangan for dengan penghitung yang diubah-ubah. doseq menjalankan efek samping untuk setiap elemen, for membangun lazy sequence (list comprehension), dan loop/recur menyatakan iterasi tanpa menambah call stack.
Fungsi
Definisikan fungsi bernama dengan defn. Sebuah fungsi dapat punya beberapa arity (jumlah argumen berbeda), dan #(...) adalah bentuk singkat untuk fungsi anonim kecil dengan % sebagai argumennya.
(defn greet
([] (greet "world"))
([name] (str "Hello, " name "!")))
(greet) ;; => "Hello, world!"
(greet "Ana") ;; => "Hello, Ana!"
(defn sum-all [& nums] (reduce + nums))
(sum-all 1 2 3) ;; => 6
(map #(* % %) [1 2 3]) ;; => (1 4 9)
(filter even? (range 10)) ;; => (0 2 4 6 8)Fungsi adalah nilai: Anda dapat meneruskannya ke map, filter, dan reduce, mengembalikannya dari fungsi lain, dan menyimpannya di dalam map.
Koleksi: list, vector, map, dan set
Empat koleksi inti Clojure bersifat immutable dan persistent: "mengubah" salah satunya menghasilkan versi baru yang berbagi sebagian besar strukturnya dengan versi lama, sehingga pembaruan murah.
| Koleksi | Literal | Penggunaan umum |
|---|---|---|
| List | '(1 2 3) | Kode dan data sekuensial yang ditambahkan di depan |
| Vector | [1 2 3] | Sequence berindeks, penambahan di akhir |
| Map | {:name "Ana" :age 30} | Record key-value |
| Set | #{:red :green} | Nilai unik, pengujian keanggotaan |
(def user {:name "Ana" :age 30})
(:name user) ;; => "Ana"
(assoc user :email "[email protected]") ;; new map with :email
(update user :age inc) ;; => {:name "Ana", :age 31}
(conj [1 2] 3) ;; => [1 2 3]
(contains? #{:red :green} :red) ;; => true
(get-in {:a {:b 1}} [:a :b]) ;; => 1
user ;; unchanged: {:name "Ana", :age 30}Fitur khas: threading macro, atom, dan macro
Threading macro membuat data pipeline terbaca dari atas ke bawah. -> menyisipkan hasil sebelumnya sebagai argumen pertama form berikutnya; ->> menyisipkannya sebagai argumen terakhir.
(->> (range 1 11)
(filter odd?)
(map #(* % %))
(reduce +)) ;; => 165
(def counter (atom 0))
(swap! counter inc)
(swap! counter + 10)
@counter ;; => 11
(defmacro unless [test & body]
`(if ~test nil (do ~@body)))
(unless false (println "runs"))Atom menyimpan state yang bisa berubah: swap! menerapkan fungsi secara atomik, sehingga aman dipakai dari beberapa thread. Ref dan agent mencakup kasus konkurensi lainnya. Karena kode adalah data (list), macro dapat mentransformasi kode sebelum dikompilasi; when, cond, dan -> sendiri adalah macro.
Penanganan error
Clojure memakai exception JVM dengan try, catch, dan finally. Untuk menyertakan data pada error, lempar ex-info dan baca kembali datanya dengan ex-data.
(defn parse-age [s]
(let [n (Long/parseLong s)]
(when (neg? n)
(throw (ex-info "Age must not be negative" {:value n})))
n))
(try
(parse-age "-5")
(catch clojure.lang.ExceptionInfo e
(println (ex-message e) (ex-data e)))
(catch NumberFormatException _
(println "not a number")))
;; Age must not be negative {:value -5}Program lengkap kecil: frekuensi kata
(ns wordcount
(:require [clojure.string :as str]))
(defn word-counts [text]
(->> (str/split (str/lower-case text) #"\W+")
(remove str/blank?)
frequencies
(sort-by val >)))
(doseq [[word n] (take 2 (word-counts "The cat and the hat and the bat"))]
(println word n))
;; the 3
;; and 2Program ini mengubah teks menjadi huruf kecil, memecahnya pada karakter non-kata dengan literal regular expression #"\W+", menghitung setiap kata dengan frequencies (yang mengembalikan map), dan mengurutkan entri berdasarkan jumlah secara menurun.
Langkah selanjutnya
- Panduan resmi di clojure.org: Getting Started, Learn Clojure, dan panduan deps.edn.
- Halaman referensi Clojure untuk sintaks reader, special form, dan struktur data.
- Siapkan editor dengan REPL yang terhubung (misalnya Calva untuk VS Code, CIDER untuk Emacs, atau Cursive untuk IntelliJ).
- Coba ClojureScript jika Anda ingin menargetkan browser.
FAQ
Apakah Clojure bahasa fungsional?
Ya. Clojure mengutamakan fungsi murni dan data immutable, tetapi bersifat pragmatis: mengizinkan efek samping dan menyediakan state mutable yang terkelola lewat atom, ref, dan agent.
Apakah saya perlu tahu Java untuk belajar Clojure?
Tidak. Anda dapat mempelajari bahasanya tanpa Java, tetapi Java runtime harus terpasang, dan sedikit pengetahuan tentang JVM membantu saat membaca stack trace atau memakai library Java.
Apa perbedaan list dan vector di Clojure?
List '(1 2 3) menambahkan elemen di depan dan terutama dipakai untuk kode; vector [1 2 3] menambahkan di akhir dan mendukung akses cepat berdasarkan indeks. Sebagian besar data dalam program Clojure disimpan dalam vector dan map.
Mengapa Clojure memakai begitu banyak tanda kurung?
Kode Clojure ditulis sebagai struktur data Lisp, sehingga setiap pemanggilan adalah sebuah list. Sintaks yang seragam inilah yang memungkinkan macro, dan editor dengan structural editing membuat tanda kurung mudah dikelola.
Apa perbedaan Clojure dan ClojureScript?
Keduanya bahasa yang sama pada host yang berbeda: Clojure berjalan di JVM, ClojureScript dikompilasi ke JavaScript. Interop dengan host berbeda, tetapi fungsi inti dan struktur datanya sama.