Differenza tra Simboli e Vars in Clojure

Sono sempre un po ‘confuso riguardo a Simboli e Vars in Clojure. Ad esempio, è sicuro dire che + è un simbolo che è usato per denotare una var, e questa var punta a un valore che è una funzione che può aggiungere numeri?

Quindi cosa succede, passo dopo passo, quando inserisco “+” in un REPL?

  1. Il simbolo viene qualificato in uno spazio dei nomi, in questo caso clojure.core
  2. Quindi in alcune tabelle dei simboli ci sono le informazioni che + si riferisce a una var
  3. Quando viene valutata questa var, il risultato è un valore di funzione?

C’è un simbolo + di cui puoi parlare citandolo:

user=> '+ + user=> (class '+) clojure.lang.Symbol user=> (resolve '+) #'clojure.core/+ 

Quindi si risolve in # +, che è un Var:

 user=> (class #'+) clojure.lang.Var 

Il Var fa riferimento all’object funzione:

 user=> (deref #'+) # user=> @#'+ # 

(Il simbolo @ è solo una scorciatoia per deref.) Naturalmente il modo usuale per ottenere la funzione è di non citare il simbolo:

 user=> + # 

Nota che i collegamenti lessicali sono un meccanismo diverso, e possono ombreggiare Vars, ma puoi ignorarli facendo esplicito riferimento al Var:

 user=> (let [+ -] [(+ 1 2) (@#'+ 1 2)]) [-1 3] 

Nell’ultimo esempio il deref può anche essere escluso:

 user=> (let [+ -] [(+ 1 2) (#'+ 1 2)]) [-1 3] 

Questo perché Var implementa IFn (l’interfaccia per le funzioni Clojure) chiamando deref su se stesso, trasmettendo il risultato a IFn e delegando la chiamata di funzione a tale.

Il meccanismo di visibilità utilizzato quando si definiscono le funzioni private con defn- si basa sui metadati sul simbolo. Puoi bypassarlo facendo riferimento direttamente al Var, come sopra:

 user=> (ns foo) nil foo=> (defn- private-function [] :secret) #'foo/private-function foo=> (in-ns 'user) # user=> (foo/private-function) java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36) user=> (#'foo/private-function) :secret 

Vedi la documentazione per gli spazi dei nomi :

I namespace sono mappature da semplici simboli (non qualificati) a Vars e / o Classi. Vars può essere internato in un namespace, usando def o una qualsiasi delle sue varianti, nel qual caso hanno un semplice simbolo per un nome e un riferimento al loro spazio dei nomi contenente, e lo spazio dei nomi mappa quel simbolo alla stessa var. Uno spazio dei nomi può anche contenere mappature da simboli a vars internati in altri spazi dei nomi usando refer o use, o da simboli a oggetti Class usando import.

Quindi in pratica i tuoi passi 1 e 2 sono unificati: gli spazi dei nomi sono le tabelle dei simboli.

E per quanto riguarda il passaggio 3: mi piace la definizione di variabili che sono combinazioni di nomi di valori. Il simbolo è il nome della variabile e la sua valutazione ne determinerà il valore.

Questa risposta non è molto diversa dalle altre, semplicemente non presuppone che inizialmente desideri imparare diverse nuove funzioni e concetti solo per capire cosa sta succedendo:

  1. + è un simbolo che si trova in clojure.core che per impostazione predefinita è accessibile al tuo codice.
  2. Se usato nel tuo codice senza intenti molto avanzati come citarlo o scoprirne la class, il clojure cercherà il Var a cui punta.
  3. Se questo Var è una funzione, quando + viene usato nella posizione di testa di un elenco, clojure proverà a chiamare tale funzione ( NullPointerException se questo Var non è stato puntato su una funzione). Se fornito come argomento per un’altra funzione, quella funzione potrebbe fare lo stesso per chiamarla. Ecco come funziona la funzione di chiamata.

ulteriori commenti da completare:

La maggior parte o tutte le lingue usano tabelle di simboli. Essendo un linguaggio alquanto dinamico, Clojure utilizza questo ulteriore livello di riferimento indiretto (simbolo → Var → funzione, piuttosto che solo funzione Simbolo →) in modo che la riscrittura dynamic di quale funzione sia legata a quale simbolo – sia più fattibile ed elegante, e questo a volte è un fonte di curiosità per i principianti.

Come altre risposte hanno un po ‘troppo enfatizzato, potresti altrimenti eseguire cose fantasiose come citare ( '+ ) per evitare la sua valutazione, o anche ispezionarlo con class e / o resolve come se fosse interessato a verificare cosa sia ( class ), o in quale spazio dei nomi si trova ( resolve ). Puoi anche colpire la var che punta su via var o #' . Generalmente fai cose fantasiose se stai scrivendo macro o se sei molto incline alla sperimentazione, specialmente quando lavori nel repl; a seconda dello stile in cui scrivi i tuoi macro, potresti effettivamente citarne parecchio.

e una illustrazione di fantasia per la persona extra-esplorativa:

Essendo un linguaggio un po ‘flessibile, il clojure espone l’api per prendere la funzione Simbolo → Var → per conto proprio. Normalmente non lo farai solo usando una funzione, perché ovviamente sarebbe noioso e ridondante, ma può essere usato qui per illustrare il processo:

 (deref (resolve '+)) 

Vale a dire, il simbolo viene prima risolto nel suo Var, quindi la cosa a cui punta il Var, è arrivata. Questo illustra solo il processo in 2 fasi per arrivare a una funzione (Simbolo → Var → funzione), che avviene dietro le quinte. Spero tu abbia evitato di leggere questa parte extra.


TL; DR

la risposta alla domanda originale è semplicemente: sì.