2010-10-04 14 views
14

Nunca pensé en esto hasta que le expliqué algún código de clojure a un compañero de trabajo que no estaba familiarizado con Clojure. Le estaba explicando let cuando le preguntó por qué usa un vector para declarar los enlaces en lugar de una lista. Realmente no tenía una respuesta para él. Pero el lenguaje no restringir su uso listas:¿Por qué se requiere un vector?

=> (let (x 1) x) 
java.lang.IllegalArgumentException: let requires a vector for its binding (NO_SOURCE_FILE:0) 

¿Por qué es esto?

+0

Imagino que es puramente legible, y al permitir el uso de un vector simplemente asegura que se mantenga el idioma. – MayDaniel

+0

(Se necesita un vector literal para casi todas las macros "dejar" y "con-") – MayDaniel

+1

Esto es como leyes naturales. Puedes razonar * que * es así, pero no * por qué *.Si la verdadera razón para esta decisión fue la legibilidad, alguna implementación del Esquema, una convención o el tipo de desayuno que Rich tuvo en ese día permanece en la oscuridad. El único que puede responder esto es el propio Rich. Para Clojure estamos en una posición afortunada, para el universo ... – kotarak

Respuesta

25

Mayor legibilidad, me imagino. Siempre que se necesiten enlaces en Clojure, un vector se usa de manera bastante consistente. Mucha gente está de acuerdo en que los vectores para enlaces hacen que las cosas fluyan mejor, y hacen que sea más fácil discernir cuáles son las vinculaciones y cuál es el código de ejecución.

Sólo por diversión:

user=> (defmacro list-let [bindings & body] `(let ~(vec bindings) [email protected])) 
#'user/list-let 
user=> (macroexpand-1 '(list-let (x 0) (println x))) 
(clojure.core/let [x 0] (println x)) 
user=> (list-let (x 0 y 1) (println x y)) 
0 1 
nil 
+4

Voto esta respuesta, ya que superó a la mía en 2 minutos ** ¡Y ** tiene código! ¡Buen espectaculo! – fogus

+3

Cierto, Hickey mencionó la razón de legibilidad en su ... conversación de concurrencia, creo que fue. –

+5

El uso de un vector literal también deja en claro que no es una llamada de función. list-let funciona, pero debe saber que la primera lista no se evalúa normalmente. Una vez que tienes la sintaxis literal del vector, puedes aprovecharla. –

11

Clojure se esforzaba mucho para ser coherente. No hay ninguna razón técnica con una lista que no se haya podido usar en let, fn, with-open, etc ... De hecho, puede crear su propia my-let lo suficientemente fácil como para usar una en su lugar. Sin embargo, aparte de sobresalir visiblemente, el vector se usa de manera consistente en todas las formas para significar "aquí hay algunas encuadernaciones". Debes esforzarte por mantener ese ideal en tu propio código.

13

Esto es un modismo de Scheme. En muchas implementaciones de Scheme, los corchetes se pueden usar indistintamente con paréntesis redondos en literales de lista. En esas implementaciones de Scheme, los corchetes se usan a menudo para distinguir listas de parámetros, listas de argumentos y enlaces de S-expressions o listas de datos.

En Clojure, los paréntesis y los corchetes significan cosas diferentes, pero se usan de la misma manera en las declaraciones vinculantes.

+0

+1: esto suena como un 'por qué' a la convención. – progo

1

mi conjetura es que es una convención

fn usado, defn usado, loop usos.

Parece que es para todo lo que se asemeja a un bloque de código que tiene algunos parámetros; más específico, los corchetes son para marcar esos parámetros

otras formas para bloques de código no lo utilizan, como if o do. no tienen ningún parámetro

1

Otra forma de pensar sobre esto es que let se deriva simplemente de lambda. Estas dos expresiones son equivalentes:

((fn [y] (+ y 42)) 10) 
(let [y 10] (+ 42 y)) 

Así como un punto de académico o de instrucción, incluso se podría escribir su propia versión muy rudimentario de let que tuvo una lista, así como un vector:

(defmacro my-let [x body] 
    (list (list `fn[(first x)] 
       `~body) 
     (last x))) 

(my-let (z 42) (* z z)) 

aunque no habría ninguna razón práctica para hacer esto.

Cuestiones relacionadas