Dlaczego Clojure ma „słowa kluczowe” oprócz „symboli”?

131

Mam przelotną wiedzę na temat innych Lispsów (szczególnie Scheme) od dawna. Ostatnio czytałem o Clojure . Widzę, że zawiera zarówno „symbole”, jak i „słowa kluczowe”. Znane mi symbole, ale nie ze słowami kluczowymi.

Czy inne Lispy mają słowa kluczowe? Czym różnią się słowa kluczowe od symboli innych niż mające inną notację (np. Dwukropki)?

Laurence Gonsalves
źródło

Odpowiedzi:

140

Oto dokumentacja Clojure dotycząca słów kluczowych i symboli.

Słowa kluczowe to symboliczne identyfikatory, które oceniają się same. Zapewniają bardzo szybkie testy równości ...

Symbole to identyfikatory, które są zwykle używane w odniesieniu do czegoś innego. Mogą być używane w formularzach programów do odwoływania się do parametrów funkcji, niech powiązania, nazwy klas i zmienne globalne ...

Słowa kluczowe są zwykle używane jako lekkie „ciągi ciągów”, np. Dla kluczy mapy mieszania lub wartości wysyłania metody multimetodowej. Symbole są zwykle używane do nazywania zmiennych i funkcji i rzadziej manipuluje się nimi bezpośrednio jako obiektami, z wyjątkiem makr i tym podobnych. Ale nic nie stoi na przeszkodzie, abyś używał symbolu wszędzie tam, gdzie używasz słowa kluczowego (jeśli nie masz nic przeciwko cytowaniu ich przez cały czas).

Najłatwiejszym sposobem zobaczenia różnicy jest przeczytanie Keyword.javai Symbol.javaw źródle Clojure. Istnieje kilka oczywistych różnic w implementacji. Na przykład symbol w Clojure może mieć metadane, a słowo kluczowe nie może.

Oprócz składni z pojedynczym dwukropkiem można użyć podwójnego dwukropka, aby utworzyć słowo kluczowe kwalifikujące się do przestrzeni nazw.

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

Common Lisp ma słowa kluczowe, podobnie jak Ruby i inne języki. Oczywiście w tych językach są nieco inne. Niektóre różnice między słowami kluczowymi Common Lisp a słowami kluczowymi Clojure:

  1. Słowa kluczowe w Clojure nie są symbolami.

    user> (symbol? :foo)  
    false
    
  2. Słowa kluczowe nie należą do żadnej przestrzeni nazw, chyba że specjalnie je zakwalifikujesz:

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

(Dziękuję Rainerowi Joswigowi za przedstawienie mi pomysłów na rzeczy do obejrzenia.)

Brian Carper
źródło
10
To wyjaśnia, jakie są różnice, ale nie wyjaśnia, dlaczego potrzebne są dwie różne konstrukcje. Czy Clojure nie mógł stworzyć czegoś z połączeniem możliwości słów kluczowych i symboli?
25
Słowa kluczowe są lekkie i mają wygodną składnię, myślę, że to wszystko. Język działałby dobrze bez nich, ale są one przyjemne i są bardzo szeroko używane. Nie można połączyć ich zdolności, ponieważ Słowa kluczowe zawsze oceniają się samoistnie (tj. Nie można ich używać jako nazw zmiennych lub funkcji), a symbole w ogóle nie zawsze mogą być samooceną.
Brian Carper
1
Wydaje się, słowa kluczowe są bardziej użyteczne jako klucze w hashmaps etc, ponieważ nie zmieniają raz oceniano: (eval (eval ':a))vs (eval (eval ''a)). Czy są inne zalety? Jeśli chodzi o wydajność, są identyczne?
kristianlm
5
(identyczne?: qwe: qwe) -> prawda. (identyczne? 'qwe' qwe) -> fałsz. Symbole używają wewnętrznego ciągu znaków, więc porównanie też jest szybkie.
desudesudesu
29

Common Lisp ma symbole słów kluczowych.

Słowa kluczowe też są symbolami.

(symbolp ':foo) -> T

Co wyróżnia słowa kluczowe:

  • : foo jest analizowane przez czytnik Common Lisp jako słowo kluczowe symbol :: foo
  • słowa kluczowe oceniają się same:: foo ->: foo
  • pakietem domowym symboli słów kluczowych jest pakiet KEYWORD: keyword: foo ->: foo
  • słowa kluczowe są eksportowane z pakietu KEYWORD
  • słowa kluczowe są stałymi, nie można przypisać innej wartości

W przeciwnym razie słowa kluczowe są zwykłymi symbolami. Zatem słowa kluczowe mogą nazywać funkcje lub mieć listy właściwości.

Pamiętaj: w Common Lisp symbole należą do pakietu. Można to zapisać jako:

  • foo, gdy symbol jest dostępny w bieżącym pakiecie
  • foo: bar, gdy symbol FOO jest eksportowany z pakietu BAR
  • foo :: bar, gdy w pakiecie BAR znajduje się symbol FOO

W przypadku symboli słów kluczowych oznacza to, że: foo, słowo kluczowe: foo i słowo kluczowe :: foo są tym samym symbolem. Dlatego te dwie ostatnie notacje zwykle nie są używane.

Tak więc: foo jest po prostu przetwarzane tak, aby znajdowało się w pakiecie KEYWORD, przy założeniu, że brak nazwy pakietu przed nazwą symbolu oznacza domyślnie pakiet KEYWORD.

Rainer Joswig
źródło
6

Słowa kluczowe to symbole, które oceniają się same, więc nie musisz pamiętać o ich cytowaniu.

Greg Hewgill
źródło
5
Czy to to? Wpisywanie: zamiast „nie wydaje się dużą wygraną, zwłaszcza, że: to dodatkowe naciśnięcie klawisza na większości klawiatur.
Laurence Gonsalves
11
Cóż, tak naprawdę to coś więcej niż tylko postać. Słowa kluczowe pozostają słowami kluczowymi po ocenie, podczas gdy symbole są oceniane do wszystkiego, z czym się wiążą. To bardziej jak różnica semantyczna, ponieważ są one zwykle używane do różnych celów.
Greg Hewgill
13
Słowa kluczowe nie są symbolami w Clojure
David Plumpton,
4

: słowa kluczowe są również traktowane specjalnie w wielu kolekcjach, pozwalając na bardzo wygodną składnię.

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

jest taki sam jak

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

to sprawia, że ​​rzeczy są trochę bardziej elastyczne

Arthur Ulfeldt
źródło
21
Dotyczy to również symboli, ('a {' a 1 'b 2}) => 1 and ({' a 1 'b 2}' b) => 2.
Jonas,
4

W przypadku słów kluczowych wartości skrótu są obliczane i zapisywane w pamięci podręcznej podczas pierwszego konstruowania słowa kluczowego. Szukając słowa kluczowego jako klucza mieszającego, po prostu zwraca wstępnie obliczoną wartość skrótu. W przypadku ciągów znaków i symboli skrót jest obliczany ponownie przy każdym wyszukiwaniu.

Ponieważ te same nazwane słowa kluczowe są zawsze identyczne, zawierają one własne wartości skrótu. Ponieważ wyszukiwanie w mapach i zestawach jest wykonywane z kluczy hash, oznacza to lepszą wydajność wyszukiwania w przypadku wielu wyszukiwań, a nie w samym wyszukiwaniu.

Ivan Pierre
źródło
0

Słowa kluczowe są globalne , symbole nie .

Ten przykład jest napisany w JavaScript, ale mam nadzieję, że pomoże to w zrozumieniu.

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

Kiedy konstruujesz symbol za pomocą Symbolfunkcji, za każdym razem otrzymujesz odrębny / prywatny symbol. Gdy poprosisz o symbol za pomocą Symbol.forfunkcji, za każdym razem otrzymasz ten sam symbol.

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

To wszystko jest takie samo.


Nazwy argumentów funkcji są lokalne. tj. nie słowa kluczowe.

(def foo (fn [x] (println x))) ; x is a symbol
(def bar (fn [x] (println x))) ; not the same x (different symbol)
John Leidegren
źródło