2013-05-23 11 views
6

Anche se posso interpretare erroneamente il concetto di omoiconicità, l'ho capito come "codice in dati".È possibile decomporre una funzione Clojure?

Quindi, posso scrivere codice come questo:

(def subject "world") 
(def helo '(str "Hello " subject)) 

A questo punto, helo è solo i dati, ma può essere eseguito come codice come questo:

(eval helo) 

che restituisce "mondo Ciao ".

posso anche continuare a trattare come dati helo:

(first helo) 
(count helo) 

che restituisce rispettivamente str e 3.

Fin qui tutto bene. Tuttavia, non appena mi avvolgo il codice in una funzione, mi sembra di perdere la capacità di trattare il codice come dati:

(defn helofn [subject] 
    (str "Hello " subject)) 

Come si decompongono helofn? Sembra che non possa trattarlo come dati; se faccio questo:

(count helofn) 

ottengo un'eccezione:

java.lang.UnsupportedOperationException: conteggio non supportato su questo tipo: user $ helofn

C'è un altro modo per decomponi helofn oppure mi sto aspettando troppo dall'omiconicità?

risposta

6

defn è solo un macro:

(macroexpand '(defn helofn [subject] 
    (str "Hello " subject))) 

(def helofn (clojure.core/fn ([subject] (str "Hello " subject)))) 

Se si definisce helofn il modo in cui è stata definita helo, sarete in grado di trattarlo come dati:

(def helofn '(fn [subject] 
    (str "Hello " subject))) 

Ora è possibile Kit chiama questa funzione:

((eval helofn) "world") 

e di trattarlo come un dato:

(count helofn) 

Ma, quando si utilizza defn macro che si associa helofn variabile con funzione compilata e non con il suo codice.

Non sono solo funzioni. Diciamo che è stato definito hello con il seguente codice:

(def helo (str "Hello " subject)) 

Ora hello è associato a "Ciao mondo" stringa e non con (str "Hello " subject) codice. Quindi, ora non c'è modo di ottenere il codice con cui è stata costruita questa stringa.

N.B. Se si desidera trattare il codice del clojure come dati, è necessario esaminare il suo codice macros. Qualsiasi codice passato a una macro viene considerato come dati e tutti i dati restituiti da una macro vengono considerati come codice.

1

sembra che non sulla base di

get a clojure function's code

e

Can you get the "code as data" of a loaded function in Clojure?

Fondamentalmente si può ottenere la fonte da una funzione definita in un file di .clj ma non c'è alcun modo affidabile per recuperare le strutture dati che hanno costruito una funzione dalla sola funzione.

EDIT: Anche io penso che ti aspetti troppo dall'omoiconicità. Il codice stesso è dato sì ma è abbastanza normale non essere in grado di recuperare il codice sorgente originale basato sull'artefatto emesso da quel codice. Come quando ho 2 non ho modo di sapere che è stato prodotto da (+ 1 1) o (- 4 2) nello stesso modo in cui una funzione è un pezzo di dati creato chiamando fn su altre strutture di dati che vengono interpretate come codice

9

Il helofn definizione è dati, ma si sta lasciando che essere valutato (proprio come si valutati in modo esplicito la lista helo). Se hai trattato la definizione nello stesso modo come helo, allora rimarrà dati, e suscettibili di qualunque trasformazioni che si desidera applicare:

(def helofndata '(defn helofn [subject] 
        (str "Hello " subject)) 

=> (second helofndata) 
helofn 
=> (eval helofndata) 
#'user/helofn 
3

Homoiconicity è un concetto molto potente e non credo che vi aspettate troppo da questo

defn è in realtà una macro che utilizza l'apposito modulo def definire una funzione, in modo da:

(defn sq [x] 
    (* x x)) 

è in realtà equivale a:

(def sq (fn ([x] (* x x)))) 

Così defn qui sta ricevendo i args sq [x] (* x x), quindi crea l'elenco (def sq (fn ([x] (* x x)))), lo restituisce come risultato della macro e viene quindi valutato. Tutto ciò avviene attraverso la manipolazione di elenchi, mappe, vettori, simboli, ecc., Tramite la macro defn.

Il fatto che in Clojure non sia possibile ottenere l'elenco originale di simboli da cui è stata definita una funzione, ha a che fare con il fatto che in Clojure tutto il codice è compilato. Questo è il motivo per cui la valutazione di (fn [x] 1) nel REPL restituisce qualcosa come #<user$eval809$fn__810 [email protected]> . Ma ancora, come menzionato in un previous answer, il codice che viene valutato è dati.

Forse sto andando troppo lontano con questo, ma se volevi avere per ogni funzione che definisci, i dati da cui è stato creato, puoi aggiungerlo ai suoi metadati creando la tua macro personalizzata.

Ecco un'implementazione naif per una macro:

(defmacro defn* [x & body ] 
    (let [form `'~&form 
     x (vary-meta x assoc :form form)] 
    `(defn ~x [email protected]))) 
;=> #'user/defn* 

(defn* sq [x] 
    (* x x)) 
;=> #'user/sq 

(:form (meta #'sq)) 
;=> (defn* sq [x] (* x x)) 

&form è un argomento implicito (insieme & env) che contiene l'intero modulo (non valutata) al quale la macro è stato chiamato (cioè i dati che è valutato dal compilatore).

Spero che questo aiuti e non porti più confusione.