Mengapa Clojure memiliki "kata kunci" di samping "simbol"?

130

Saya memiliki pengetahuan yang lewat tentang Lisps lain (khususnya Skema) dari jalan kembali. Baru-baru ini saya telah membaca tentang Clojure . Saya melihat bahwa ia memiliki "simbol" dan "kata kunci". Simbol yang saya kenal, tetapi tidak dengan kata kunci.

Apakah Lisps lain memiliki kata kunci? Bagaimana kata kunci berbeda dari simbol selain memiliki notasi yang berbeda (yaitu: titik dua)?

Laurence Gonsalves
sumber

Jawaban:

139

Berikut dokumentasi Clojure untuk Kata Kunci dan Simbol.

Kata kunci adalah pengidentifikasi simbolis yang mengevaluasi diri mereka sendiri. Mereka menyediakan tes kesetaraan yang sangat cepat ...

Simbol adalah pengidentifikasi yang biasanya digunakan untuk merujuk ke sesuatu yang lain. Mereka dapat digunakan dalam bentuk program untuk merujuk ke parameter fungsi, biarkan binding, nama kelas dan vars global ...

Kata kunci umumnya digunakan sebagai "string konstan" yang ringan, misalnya untuk kunci peta hash atau nilai pengiriman multimethod. Simbol umumnya digunakan untuk memberi nama variabel dan fungsi dan itu kurang umum untuk memanipulasi mereka sebagai objek langsung kecuali dalam makro dan semacamnya. Tetapi tidak ada yang menghentikan Anda dari menggunakan simbol di mana pun Anda menggunakan kata kunci (jika Anda tidak keberatan mengutipnya sepanjang waktu).

Cara termudah untuk melihat perbedaannya adalah membaca Keyword.javadan Symbol.javasumber Clojure. Ada beberapa perbedaan implementasi yang jelas. Misalnya Simbol di Clojure dapat memiliki metadata dan Kata Kunci tidak bisa.

Selain sintaks kolon tunggal, Anda dapat menggunakan kolon ganda untuk membuat kata kunci yang memenuhi syarat namespace.

user> :foo
:foo
user> ::foo
:user/foo

Lisp umum memiliki kata kunci, seperti halnya Ruby dan bahasa lainnya. Mereka sedikit berbeda dalam bahasa-bahasa itu tentu saja. Beberapa perbedaan antara kata kunci Common Lisp dan kata kunci Clojure:

  1. Kata kunci dalam Clojure bukan Simbol.

    user> (symbol? :foo)  
    false
    
  2. Kata kunci bukan milik ruang nama apa pun kecuali Anda secara khusus memenuhi syarat:

    user> (namespace :foo)
    nil
    user> (namespace ::foo)
    "user"
    

(Terima kasih Rainer Joswig karena memberi saya ide tentang berbagai hal untuk dilihat.)

Brian Carper
sumber
10
Ini menjelaskan apa perbedaannya, tetapi bukan mengapa dua konstruksi yang berbeda diperlukan. Tidak bisakah Clojure menciptakan sesuatu dengan penyatuan kemampuan Kata Kunci dan Simbol?
25
Kata kunci yang ringan dan memiliki sintaks yang mudah digunakan, saya pikir hanya itu yang ada. Bahasa akan berfungsi dengan baik tanpa mereka tetapi mereka baik untuk memiliki dan mereka sangat banyak digunakan. Anda tidak dapat memiliki gabungan kemampuan mereka karena Kata kunci selalu mengevaluasi diri sendiri (yaitu Anda tidak dapat menggunakannya sebagai nama variabel atau fungsi) dan Simbol pada umumnya tidak selalu dapat mengevaluasi diri sendiri.
Brian Carper
1
Tampaknya kata kunci yang lebih berguna sebagai kunci dalam hashmaps dll karena mereka tidak berubah setelah dievaluasi: (eval (eval ':a))vs (eval (eval ''a)). Apakah ada kelebihan lain? Kinerja bijaksana, mereka identik?
kristianlm
5
(identik?: qwe: qwe) -> true. (identik? 'qwe' qwe) -> false. Simbol menggunakan string internal, jadi perbandingan juga cepat.
desudesudesu
29

Lisp umum memiliki simbol kata kunci.

Kata kunci juga simbol.

(symbolp ':foo) -> T

Apa yang membuat kata kunci istimewa:

  • : foo diuraikan oleh pembaca Common Lisp sebagai kata kunci simbol :: foo
  • kata kunci mengevaluasi untuk diri mereka sendiri:: foo ->: foo
  • paket awal simbol kata kunci adalah paket KEYWORD: kata kunci: foo ->: foo
  • kata kunci diekspor dari paket KEYWORD
  • kata kunci adalah konstanta, tidak diizinkan untuk memberikan nilai yang berbeda

Kalau tidak, kata kunci adalah simbol biasa. Jadi kata kunci dapat memberi nama fungsi atau memiliki daftar properti.

Ingat: dalam Common Lisp, simbol milik sebuah paket. Ini dapat ditulis sebagai:

  • foo, ketika simbol dapat diakses dalam paket saat ini
  • foo: bar, ketika simbol FOO diekspor dari paket BAR
  • foo :: bar, ketika simbol FOO ada di dalam paket BAR

Untuk simbol kata kunci yang berarti: foo, kata kunci: foo dan kata kunci :: foo semuanya adalah simbol yang sama. Jadi dua notasi terakhir biasanya tidak digunakan.

Jadi: foo hanya diuraikan dalam paket KEYWORD, dengan asumsi bahwa tidak memberikan nama paket sebelum nama simbol berarti secara default paket KEYWORD.

Rainer Joswig
sumber
6

Kata kunci adalah simbol yang mengevaluasi untuk diri mereka sendiri, jadi Anda tidak harus ingat untuk mengutipnya.

Greg Hewgill
sumber
5
Itu saja? Mengetik: daripada 'tidak tampak seperti kemenangan besar, terutama karena: adalah penekanan tombol ekstra pada kebanyakan keyboard.
Laurence Gonsalves
11
Yah, itu lebih dari sekedar karakter, sungguh. Kata kunci tetap kata kunci setelah evaluasi, sementara simbol dievaluasi untuk apa pun yang mereka ikat. Ini lebih seperti perbedaan semantik, karena mereka biasanya digunakan untuk tujuan yang berbeda.
Greg Hewgill
13
Kata kunci bukan simbol di Clojure
David Plumpton
4

: kata kunci juga diperlakukan secara khusus oleh banyak koleksi, memungkinkan beberapa sintaks yang sangat nyaman.

(:user-id (get-users-map))

sama dengan

((get-users-map) :user-id)

ini membuat semuanya sedikit lebih fleksibel

Arthur Ulfeldt
sumber
21
Ini juga berlaku untuk simbol, ('a {' a 1 'b 2}) => 1 dan ({' a 1 'b 2}' b) => 2.
Jonas
4

Untuk kata kunci, nilai hash dihitung dan di-cache ketika kata kunci pertama kali dibuat. Saat mencari kata kunci sebagai kunci hash, itu hanya mengembalikan nilai hash yang dihitung sebelumnya. Untuk string dan simbol, hash dihitung ulang pada setiap pencarian.

Mengapa kata kunci dengan nama yang sama selalu identik, mereka berisi nilai hash mereka sendiri. Karena pencarian di peta dan set dibuat dari kunci hash, ini meningkatkan efisiensi pencarian yang lebih baik jika banyak pencarian, bukan dalam pencarian itu sendiri.

Ivan Pierre
sumber
0

Kata kunci bersifat global , simbol tidak .

Contoh ini ditulis dalam JavaScript, tapi saya harap ini membantu untuk menjelaskan maksudnya.

const foo = Symbol.for(":foo") // this will create a keyword
const foo2 = Symbol.for(":foo") // this will return the same keyword
const foo3 = Symbol(":foo") // this will create a new symbol
foo === foo2 // true
foo2 === foo3 // false

Ketika Anda membangun simbol menggunakan Symbolfungsi Anda mendapatkan simbol yang berbeda / pribadi setiap kali. Ketika Anda meminta simbol melalui Symbol.forfungsi, Anda akan mendapatkan kembali simbol yang sama setiap kali.

(println :foo) ; Clojure
System.out.println(RT.keyword(null, "foo")) // Java
console.log(System.for(":foo")) // JavaScript

Semuanya sama saja.


Nama argumen fungsi adalah lokal. yaitu bukan kata kunci.

(def foo (fn [x] (println x))) ; x is a symbol
(def bar (fn [x] (println x))) ; not the same x (different symbol)
John Leidegren
sumber