tutorial online example clojure

online - clojure windows



Explica los símbolos de Clojure (3)

Tengo un símbolo "a" vinculado a una función:

(defn a [] (println "Hello, World")) user=> a #<user$a__292 user$a__292@97eded> user=> (a) Hello, World nil

Luego utilizo syntax-quote, "resuelve el símbolo en el contexto actual, produciendo un símbolo totalmente calificado", según la documentación de Clojure . ¿Pero por qué no puedo usarlo de la misma manera que un símbolo no calificado?

user=> `a user/a user=> (`a) java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)

Segunda pregunta: si tengo un símbolo en una lista, ¿por qué no puedo evaluarlo de la misma manera que si evaluara el símbolo directamente?

user=> (def l ''(a 1 2)) #''user/l user=> ''l l user=> (first l) a user=> ((first l)) java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)

Tengo la sospecha de que tengo un defecto fatal en algún lugar de la comprensión fundamental de cómo funcionan los símbolos aquí. ¿Qué está mal con el código anterior?


usuario => (def l ''(a 1 2)) usuario => ((primer l))

Convierta esto en:

usuario => (def l `(~ a 1 2))

El ~ aquí resuelve el símbolo a a su var correspondiente, y el backtick hace que el anulación de un nombre funcione.

En general, debes entender la diferencia entre vars (que están ligados a algo) y símbolos (que nunca están ligados a nada).

Trataré de explicarlo (con la esperanza de que mi explicación no te confunda más):

user=> (def v "content") #''user/content

-> define una var en el espacio de nombres actual bajo el símbolo ''v (totalmente calificado'' usuario / v, asumiendo que este es el espacio de nombre actual), y lo vincula (la var, no el símbolo) al objeto "contenido".

user=> v "content"

-> resuelve v a la var, y obtiene el valor encuadernado

user=> #''v #''user/v

-> se resuelve en la var misma

user=> ''v v

-> no resuelve nada, solo un símbolo simple (lamentablemente, el REPL no lo indica, imprimiendo ''v como v)

user=> `v user/v

-> como ya se ha citado, se resuelve en el símbolo en el contexto actual (espacio de nombres), pero el resultado sigue siendo un símbolo (completamente calificado), no el usuario var / v

user=> ''(v) (v)

-> simple cita, no resuelve nada

user=> `(v) (user/v)

-> sintaxis-quote, lo mismo que comillas, pero resuelve símbolos para símbolos calificados para espacios de nombres

user=> `(~v) ("content")

-> resolver el símbolo a su var (que se desreferencia implícitamente), rindiendo su objeto encuadernado


REPL = leer el ciclo de impresión de evaluación. Paso a paso por el proceso read-eval.

LEER: Clojure ve la cadena "(`a)" , lo analiza y termina con una estructura de datos. En el momento de la lectura, las macros de los lectores se expanden y no sucede mucho más. En este caso, el lector expande la cita hacia atrás y termina con esto:

user> (read-string "(`a)") ((quote user/a))

EVAL: Clojure intenta evaluar este objeto. Las reglas de evaluación varían según el tipo de objeto que estés viendo.

  • Algunos objetos se evalúan como ellos mismos (números, cadenas, palabras clave, etc.).
  • Un símbolo se evalúa resolviéndolo en algún espacio de nombre para obtener algún valor (generalmente).
  • Una lista se evalúa expandiendo macro la lista hasta que no queden macros, luego evalúa recursivamente el primer elemento en la lista para obtener algún valor resultante , luego usa el valor del primer elemento de la lista para decidir qué hacer. Si el primer valor es una forma especial, sucede algo especial. De lo contrario, el primer valor se trata como una función y se llama con los valores del resto de la lista (obtenida mediante la evaluación recursiva de todos los elementos de la lista) como parámetros.
  • etc.

Consulte clojure.lang.Compiler/analyzeSeq en la fuente de Clojure para ver las reglas de evaluación para listas, o clojure.lang.Compiler/analyzeSymbol para símbolos. Hay muchas otras reglas de evaluación allí.

Ejemplo

Supongamos que haces esto:

user> (user/a)

El REPL termina haciendo esto internamente:

user> (eval ''(user/a))

Clojure ve que está evaluando una lista, por lo que evalúa todos los elementos de la lista. El primer (y único) elemento:

user> (eval ''user/a) #<user$a__1811 user$a__1811@82c23d>

a no es una forma especial y esta lista no necesita macroexpandirse, por lo que el símbolo a se busca en el user del espacio de nombres y el valor resultante aquí es un fn . Entonces se llama esta fn .

Tu codigo

Pero en cambio tienes esto:

user> (eval ''((quote user/a)))

Clojure evalúa el primer elemento de la lista, que a su vez es una lista.

user> (eval ''(quote user/a)) user/a

Evaluó el primer elemento de esta sublista, quote , que es una forma especial, por lo que se aplican reglas especiales y devuelve su argumento (el Símbolo a ) no evaluado.

El símbolo a es el valor en este caso, ya que fn era el valor anterior. Entonces, Clojure trata el Símbolo como una función y lo llama. En Clojure, cualquier cosa que implemente la interfaz Ifn se puede Ifn como un fn . Ocurre que clojure.lang.Symbol implementa Ifn . Un Símbolo llamado como función espera un parámetro, una colección, y se ve a sí mismo en esa colección. Está destinado a ser usado así:

user> (''a {''a :foo}) :foo

Esto es lo que trata de hacer aquí. Pero no está pasando ningún parámetro, por lo que aparece el error "Número incorrecto de argumentos pasados ​​a: Símbolo" (se espera una colección).

Para que funcione tu código necesitarás dos niveles de eval . Esto funciona, espero que puedas ver por qué:

user> (eval ''((eval (quote user/a)))) Hello, world user> ((eval (first l))) Hello, world

Tenga en cuenta que en el código real, el uso directo de eval es generalmente una mala idea. Las macros son una mejor idea de lejos. Solo lo estoy usando aquí para la demostración.

Busca en Compiler.java en la fuente Clojure para ver cómo se desarrolla todo esto. No es muy difícil de seguir.


Usar un Símbolo como una función no es lo mismo que evaluarlo. Los símbolos como funciones funcionan de la misma manera que las palabras clave como funciones. Me gusta esto:

user=> (declare a) #''user/a user=> (def a-map {''a "value"}) #''user/a-map user=> (''a a-map) "value" user=>

Esta no es la forma en que normalmente usarías un símbolo. Se usan más comúnmente para buscar vars en un espacio de nombres y cuando se genera código en una macro.

Para desglosar las capas de indirección, definamos "x" como 1 y veamos qué sucede:

user=> (def x 1) #''user/x

Usando def , hemos creado una "var". El nombre de la var es el símbolo user / x. La forma especial def devuelve la var misma a la réplica, y esto es lo que podemos ver impreso. Probemos y conservemos esa var:

user=> #''x #''user/x

La sintaxis #'' es una macro de lector que dice "dame la var que se refiere al siguiente símbolo". Y en nuestro caso, ese símbolo es "x". Recuperamos la misma var como antes. Los Vars son indicadores de los valores, y pueden ser desreferenciados:

user=> (deref #''x) 1

Pero la var necesita ser encontrada antes de que pueda ser desreferenciada. Aquí es donde entran en juego la capacidad de los símbolos. Un espacio de nombres es como un mapa, donde los símbolos son claves y los vars son los valores, y cuando llamábamos claramente un símbolo, implícitamente buscamos su var en nuestro espacio de nombres. Me gusta esto:

user=> (''x (.getMappings *ns*)) #''user/x

Aunque, en realidad, probablemente sea más como esto:

user=> (.findInternedVar *ns* ''x) #''user/x

Y ahora hemos cerrado el círculo en el viaje del símbolo sin comillas:

user=> (deref (.findInternedVar *ns* ''x)) 1 user=> x 1

Sin embargo, los dos no son del todo iguales. Porque el evaluador hace esto para todos los símbolos, incluidos deref y * ns *.

Lo importante de las citas es que básicamente elude todo este mecanismo y recupera el símbolo simple. Al igual que la macro del lector #'' obtener macros simples, las macros `y ''lectores recuperarán símbolos simples, con o sin una calificación de espacio de nombres, respectivamente:

user=> ''x x user=> `x user/x