È giusto dire che "i macro non compongono"?

7

Su questo post del blog aphyr (che è un brillante programmatore) afferma :

Clojure macros come with some important restrictions. Because they’re expanded prior to evaluation, macros are invisible to functions. They can’t be composed functionally–you can’t (map or ...), for instance.

Il esempio classico di questo è:

(reduce and [true true false true])
;RuntimeException

Tuttavia possiamo scrivere:

(reduce #(and %1 %2) [true true false true])
;false

I 'macro' non compongono 'un reclamo valido? Sicuramente l'unico punto valido è che le macro non sono funzioni di prima classe e non dovrebbero essere trattate come tali.

    
posta hawkeye 28.12.2013 - 23:34
fonte

1 risposta

7

Penso che questo dipenda da cosa intendi per "comporre". Direi che è quasi sempre vero ma può essere preso troppo lontano.

Per prima cosa, nota che nella citazione che hai citato aphyr non ha detto "i macro non compongono"; la frase era "non può essere composta funzionalmente". Questa rivendicazione più limitata è indiscutibilmente vera. Le macro in Clojure non sono valori [1]. Non possono essere passati come argomenti ad altre funzioni o macro, non possono essere restituiti come risultato di calcoli e non possono essere memorizzati in strutture di dati.

Ci sono, naturalmente, modi per ovviare a queste limitazioni. Oltre al tuo esempio di #(and %1 %2) , c'è un senso in cui il codice seguente rappresenta la composizione.

(ns some.macros
  (:refer-clojure :exclude [when when-not]))

(defmacro when [test & body]
  '(cond ~test (do ~@body)))

(defmacro when-not [test & body]
  '(when (not ~test) ~@body))

Ho intenzionalmente utilizzato cond anziché if nella definizione di when perché cond è a sua volta una macro.

Ma, tornando al punto più grande, #(and %1 %2) non è in realtà un argomento che le macro compongono, è un argomento che puoi usare macro all'interno delle funzioni. E, poiché abbiamo molti modi potenti di lavorare con le funzioni, questo è generalmente il modo migliore per andare. Usa le macro (quando le funzioni non sono appropriate) per astrarre il codice ripetitivo, ma, quando possibile, esponi la maggior parte delle API possibili come funzioni in modo che possano essere memorizzate in mappe hash, mappate su sequenze di widget , negato con complement , giustapposto con juxt e così via.

[1] Sarebbe difficile che sia diversamente. Immaginate se le macro fossero valori e potessero essere passati come argomenti alle funzioni. Come è possibile compilare il codice che ha utilizzato tale macro, dato che la sua espansione non sarebbe stata nota fino al runtime (e potrebbe variare tra le invocazioni della funzione)?

Addendum

Come punto di interesse, considera che le funzioni sono delle macro, sono trattate appositamente dal compilatore.

Controlla questo:

some.macros> (defn when [&form &env test & body]
               '(cond ~test (do ~@body)))
#<Var@1a37b98:
  #<macros$eval7806$when__7807 some.macros$eval7806$when__7807@d1cf9c>>
some.macros> (when nil nil true 1 2)
(clojure.core/cond true (do 1 2))
some.macros> (. #'when (setMacro))
nil
some.macros> (when true 1 2)
2

Questo è, in effetti, esattamente come funziona defmacro . (Vedi qui per una spiegazione degli argomenti pseudo-argomenti &form e &env ).

    
risposta data 29.12.2013 - 04:33
fonte

Leggi altre domande sui tag