2010-09-23 12 views
30

Poszukuję możliwości drukowania przez REPL bieżącej definicji funkcji. Czy jest jakiś sposób to zrobić?Jak wyświetlić definicję funkcji w Clojure na REPL?

Na przykład, biorąc pod uwagę:

(defn foo [] (if true "true")) 

Chciałbym powiedzieć coś podobnego

(print-definition foo) 

i coś wzdłuż linii

(foo [] (if true "true")) 

drukowanych.

Odpowiedz

19

Alternatywą source (które powinny być dostępne za pośrednictwem clojure.repl/source podczas uruchamiania rEPL, od 1.2.0. Jeśli pracujesz z 1.1.0 lub niższe, source jest w clojure.contrib.repl-utils.), Do stosowania REPL, zamiast patrzeć na funkcje zdefiniowane w .clj pliku:

(defmacro defsource 
    "Similar to clojure.core/defn, but saves the function's definition in the var's 
    :source meta-data." 
    {:arglists (:arglists (meta (var defn)))} 
    [fn-name & defn-stuff] 
    `(do (defn ~fn-name [email protected]) 
     (alter-meta! (var ~fn-name) assoc :source (quote ~&form)) 
     (var ~fn-name))) 

(defsource foo [a b] (+ a b)) 

(:source (meta #'foo)) 
;; => (defsource foo [a b] (+ a b)) 

prosty print-definition:

(defn print-definition [v] 
    (:source (meta v))) 

(print-definition #'foo) 

#' jest tylko reader macro, poszerzając od #'foo do (var foo):

(macroexpand '#'reduce) 
;; => (var reduce) 
+0

Lepiej opuścić głowę, aby pozbyć się słowa kluczowego defsource. (alter-meta! (Var ~ fn-name) assoc: source (drop 1 (quote ~ & form)) – haijin

18

Będziemy chcieli, aby zaimportować nazw repl i użyć funkcji source od niego:

(ns myns 
    (:use [clojure.repl :only (source)])) 
(defn foo [] (if true "true")) 
(source foo) 

=> (foo [] (if true "true")) 
    nil 

Choć to nie będzie działać w REPL, tylko wtedy, gdy funkcja jest zdefiniowana w pliku .clj w ścieżce klas. Które nie odpowiada na twoje pytanie, wtedy: musisz mieć defn, który przechowuje, w metadanych fn określa, źródło tej funkcji. Wtedy napiszesz funkcję przypominającą nieco metadanych. To nie powinno być strasznie trudne.

4

Właśnie zadałem to pytanie na liście dyskusyjnej Clojure, a odpowiedzi obejmowały nadpisanie części REPL, aby ukryć dane wejściowe (i wyjściowe), aby można je było wykorzystać w przyszłości, a także nadpisanie opcji defn do przechowywania źródła w metadanych (które następnie można łatwo odzyskać w REPL).

Read the thread on the Clojure mailing list

+0

-1 Ta odpowiedź mogłaby być lepsza, gdybyś zawarł w niej bardziej szczegółowe wyjaśnienie, patrz [meta] (http://meta.stackexchange.com/a/8259/180500). Ta odpowiedź nie dostarcza konkretnej recepty. –

11

W Clojure 1.2 za REPL, funkcja source jest natychmiast dostępny. Można go używać w ten sposób:

$ java -cp clojure.jar clojure.main 
Clojure 1.2.0 
user=>(source slurp) 
(defn slurp 
    "Reads the file named by f using the encoding enc into a string 
    and returns it." 
    {:added "1.0"} 
    ([f & opts] 
    (let [opts (normalize-slurp-opts opts) 
      sb (StringBuilder.)] 
     (with-open [#^java.io.Reader r (apply jio/reader f opts)] 
     (loop [c (.read r)] 
      (if (neg? c) 
      (str sb) 
      (do 
       (.append sb (char c)) 
       (recur (.read r))))))))) 
nil 
user=>

Kilka innych funkcje są również automatycznie importowane do rEPL za user nazw z clojure.repl bibliotece. Zobacz dokumentacja API here.

Jednak, jak wskazano w innych odpowiedziach tutaj, nie można użyć funkcji source, tak jak w przypadku funkcji drukowania zdefiniowanych w REPL.

13

Clojure nie posiada decompiler, więc to znaczy, nie ma sposobu, aby dostać się do źródła dowolnej funkcji, chyba że był to defn ładowane z dysku.Możesz jednak użyć czystego hackowania o nazwie serializable-fn, aby utworzyć funkcję, która ma swoją formę źródłową zapisaną w metadanych: http://github.com/Seajure/serializable-fn

Odpowiedź defsource jest bardzo podobna do tej, ale to rozwiązanie działa z dowolnymi fns, a nie tylko najwyższy poziom defns. Dzięki temu fns drukuje ładnie w replach bez specjalnej funkcji drukowania.

Powiązane problemy