Files
paradigms/clojure/examples/0_1_magic.clj
2026-04-13 20:12:01 +03:00

87 lines
2.8 KiB
Clojure

(defn lecture [name]
(println)
(let [line (clojure.string/join (repeat (+ 16 (count name)) "="))]
(println line)
(println "=== Lecture" name "===")
(println line)))
(defn chapter [name]
(println)
(println "==========" name "=========="))
(defn section [name]
(println)
(println "---" name "---"))
(defn- safe-eval [expression]
(try
(eval expression)
(catch Throwable e e)))
(defmacro with-out-str-and-value [body]
`(let [s# (new java.io.StringWriter)]
(binding [*out* s#]
(let [v# (safe-eval ~body)]
(vector (str s#) v#)))))
(defn- init [seq]
(take (dec (count seq)) seq))
(defn- prepend [prefix]
(partial str prefix))
(defn- lines [indent & lines]
(apply str (map (prepend indent) (flatten lines))))
(defn- lines-collection [indent open close f value]
(let [items (mapv f value)]
(case (count items)
0 (str open close)
1 (str open (first items) close)
(lines indent
(str open (first items))
(map (prepend " ") (init (rest items)))
(str " " (last items) close)))))
(defn- remove-generated [value]
(clojure.string/replace value #"__[0-9]+#" "#"))
(defn- render [value]
(cond
(fn? value) (str "#function[" (clojure.string/replace (str (type value)) #"fn__[0-9]+" "fn") "]")
(delay? value) (str "#delay")
(instance? clojure.lang.Namespace value) (str "#namespace[" (ns-name value) "]")
(instance? Throwable value) (str (.getSimpleName (type value)) ": " (.getMessage value))
:else (remove-generated (pr-str value))))
(defn- prettify
([value] (prettify value "\n "))
([value indent]
(let [r-value (render value)]
(cond
(< (count (str indent r-value)) 80) r-value
(vector? value) (lines-collection indent "[" "]" #(clojure.string/triml (prettify % (str indent " "))) value)
(seq? value) (lines-collection indent "(" ")" #(clojure.string/triml (prettify % (str indent " "))) value)
(map? value) (lines-collection
indent "{" "}"
(fn [[key value]] (str (render key) " " (prettify value (str indent " "))))
value)
:else r-value))))
(defn example' [description & expressions]
{:pre [(not (empty? expressions))]}
(println (str " " description ": "))
(letfn [(run [expression]
(let [[output value] (with-out-str-and-value expression)]
(println " " (render expression) "->" (prettify value))
(if-not (empty? output)
(mapv #(println " >" %) (clojure.string/split-lines output)))))]
(mapv run expressions)))
(defmacro example [description & expressions]
`(apply example' ~description (quote ~expressions)))
(defn with-in-file [file action]
(with-in-str (slurp file) (action)))