Seperti yang sudah Anda perhatikan, fakta bahwa sifat tidak stabil tidak dianjurkan di Clojure tidak berarti bahwa itu dilarang dan tidak ada konstruksi yang mendukungnya. Jadi Anda benar bahwa menggunakan def
Anda dapat mengubah / memutasi pengikatan di lingkungan dengan cara yang mirip dengan tugas yang dilakukan dalam bahasa lain (lihat dokumentasi Clojure pada vars ). Dengan mengubah binding di lingkungan global Anda juga mengubah objek data yang menggunakan binding ini. Sebagai contoh:
user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101
Perhatikan bahwa setelah mendefinisikan kembali pengikatan x
, fungsinya f
juga berubah, karena tubuhnya menggunakan pengikatan itu.
Bandingkan ini dengan bahasa yang mendefinisikan ulang suatu variabel tidak menghapus ikatan lama tetapi hanya membayangi saja, yaitu membuatnya tidak terlihat dalam lingkup yang muncul setelah definisi baru. Lihat apa yang terjadi jika Anda menulis kode yang sama di SML REPL:
- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int
Perhatikan bahwa setelah definisi kedua dari x
, fungsi f
masih menggunakan pengikatan x = 1
yang ada di ruang lingkup ketika itu didefinisikan, yaitu pengikatan val x = 100
tidak menimpa pengikatan sebelumnya val x = 1
.
Intinya: Clojure memungkinkan untuk mengubah lingkungan global dan mendefinisikan kembali ikatan di dalamnya. Mungkin untuk menghindari ini, seperti bahasa lain seperti SML, tetapi def
konstruk di Clojure dimaksudkan untuk mengakses dan mengubah lingkungan global. Dalam praktiknya, ini sangat mirip dengan tugas yang dapat dilakukan dalam bahasa imperatif seperti Java, C ++, Python.
Meski begitu, Clojure menyediakan banyak konstruksi dan perpustakaan yang menghindari mutasi, dan Anda bisa datang jauh tanpa menggunakannya sama sekali. Menghindari mutasi sejauh ini merupakan gaya pemrograman yang disukai di Clojure.