2011-11-19 13 views
13

En una forma let (Clojure aquí) que puedo hacer algo como¿Por qué no hay destrucción en forma de def?

(let [[u s v] (svd A)] 
    (do-something-with u v)) 

donde svd devuelve una lista de longitud tres. Este es un tipo muy natural de que hay que hacer, ¿por qué no es que no tenemos

(def [u s v] (svd A)) 

y sus diversas generalizaciones como el comportamiento por defecto de la forma def? No veo cómo esto podría interferir con algo que def ya está haciendo. ¿Puede alguien que entiende el Zen of Lisp o Clojure explicar por qué def no admite el enlace (con desestructuración) tan poderoso como let?

Respuesta

15

def es una forma especial en el nivel de compilador: hace un Var. def tiene que estar disponible y usarse antes de que la desestructuración esté disponible. Verá algo similar con let*, una primitiva de compilación que no admite desestructuración: luego, después de varios miles de líneas en clojure/core.clj, el lenguaje es finalmente lo suficientemente potente como para proporcionar una versión de let con desestructuración, como una macro encima de let*.

Si lo desea, puede escribir una macro (por ejemplo, def+) que lo haga por usted. Personalmente creo que es un poco asqueroso y no lo usaría, pero usar un Lisp significa usar un lenguaje que se adapta a ti personalmente.

+1

Creo que este es el tipo de respuesta que me interesaba. A riesgo de editorialización excesiva de mi parte, supongo que estás diciendo que la razón por la que esto no se hace en Clojure es en parte técnica (en esa 'def' resulta ser una compilador primitivo), y en parte por convención (en ese caso (por ejemplo, Rich Hickey) podría haber comenzado con un primitivo 'def' * y declarado' def' más tarde en algún punto del núcleo). –

+1

@GabrielMitchell sí, eso hubiera sido posible. Pero es mucho menos útil para 'def' que para 'let', y carecería de simetría. 'let' ** always ** toma un vector y las destrucciones dentro de eso; hacer que 'def' lo haga sería mucho menos conveniente, y hacer que def acepte un símbolo o una forma de desestructuración es bastante horrible. – amalloy

+0

¿Puedes decir algo más sobre por qué un macro sería tan bruto? En este momento, estoy pensando que es una gran idea, pero, en base a su comentario, también me pregunto si de alguna manera iría en contra de Clojure. Por lo general, es mejor ir con el grano del lenguaje que armar algo que vaya en contra de él, pero parece atractivo para alguien sin experiencia en el idioma. El hecho de que después de 10 años, el uso de ese macro no sea un lugar común, me hace preguntarme si es una solución torpe a un problema que se resuelva de otra manera. –

2

def es básicamente el constructor de Vars. El primer argumento es el símbolo que nombra al Var. Toma ese símbolo y devuelve un Var para ese símbolo. La desestructuración cambiaría esta semántica.

Sin embargo, podría escribir una macro que lo haga.

+1

¿No es lo que dijiste igualmente cierto para 'let'? Quiero decir, ¿no se podría hacer el mismo argumento para decir que 'let' no debería soportar un bind destructivo? – sepp2k

+1

Sí, sepp2k saca a colación el punto que me pregunto. Por lo que yo entiendo, la diferencia básica entre 'def' y' let' es el alcance de los enlaces, y aún 'let' parece tener un comportamiento más general. Como señala Chuck, el hecho de que pudieras escribir una macro polimórfica en el primer argumento (un comportamiento para un solo símbolo, otro comportamiento para las listas de símbolos) me hace preguntarme por qué esta no es la implementación predeterminada. –

0

Esto no es perfecto, pero es empezar a escribir un def+ https://clojuredocs.org/clojure.core/destructure

(defmacro def+ 
    "binding => binding-form 
    internalizes binding-forms as if by def." 
    {:added "1.9", :special-form true, :forms '[(def+ [bindings*])]} 
    [& bindings] 
    (let [bings (partition 2 (destructure bindings))] 
    (sequence cat 
     ['(do) 
     (map (fn [[var value]] `(def ~var ~value)) bings) 
     [(mapv (fn [[var _]] (str var)) bings)]]))) 

Con que se puede hacer ...

(def+ [u s v] [1 5 9], foo "bar") 

... sin comprometer la simplicidad de def ...

(def+ foo "bar") 

.. .que es lo que se solicitó y sugirió. Esto todavía tiene el problema de introducir variables gensym en el espacio de nombres global. El problema de gensym podría ser manejado pero dado el caso de uso (usar en repl) las variables adicionales son probablemente aceptables.

1

A continuación se presentan algunas justificaciones filosóficas.

Clojure favorece la inmutabilidad sobre la mutabilidad, y todas las fuentes de mutabilidad deben considerarse y nombrarse cuidadosamente. def crea variables vars. Idiomatic Clojure por lo tanto, no los usa mucho de todos modos, y tampoco querría que fuera demasiado fácil crear muchos variables mutables sin cuidado (por ejemplo, mediante desestructuración). let y la desestructuración de argumentos de funciones, sin embargo, crea enlaces inmutables, por lo que Clojure hace que esos enlaces sean fáciles de crear.

Vars creados por def tienen alcance global. Por lo tanto, debe nombrar def ed vars cuidadosamente y mantenerlos en pocos. Destructuring def haría que sea muy fácil crear muchos def s sin cuidado. let y la desestructuración de argumentos de función, por otro lado, crea enlaces locales de ámbito léxico, por lo que la conveniencia de la desestructuración no causa contaminación de nombres.

Cuestiones relacionadas