Хамза Ерликая уже сделал самый важный вывод: код Clojure всегда компилируется. Я просто добавляю иллюстрацию и некоторую информацию о небольшом количестве фруктов для ваших усилий по оптимизации.
Во-первых, вышеприведенный пункт о том, что код Clojure всегда компилируется, включает в себя замыкания, возвращаемые функциями и функциями высшего порядка, созданными путем вызова eval
в fn
/ fn*
формах, и действительно всего, что может действовать как функция Clojure. , Таким образом, вам не нужен отдельный DSL для описания функций, просто используйте функции более высокого порядка (и, возможно, макросы):
(defn make-affine-function [a b]
(fn [x] (+ (* a x) b)))
((make-affine-function 31 47) 5)
; => 202
Вещи были бы более интересными, если бы ваши спецификации включали информацию о типах параметров, так как тогда вы могли бы быть заинтересованы в написании макроса для генерации кода с использованием этих подсказок типов. Простейшим примером, который я могу придумать, был бы вариант выше:
(defmacro make-primitive-affine-function [t a b]
(let [cast #(list (symbol (name t)) %)
x (gensym "x")]
`(fn [~x] (+ (* ~(cast a) ~(cast x)) ~(cast b)))))
((make-primitive-affine-function :int 31 47) 5)
; => 202
Используйте :int
, :long
, :float
или :double
(или символы, не соответствующие пространству имен соответствующих имен) в качестве первого аргумента, чтобы воспользоваться преимуществами неупакованной примитивной арифметики, подходящей для ваших типов аргументов. В зависимости от того, что делает ваша функция, это может значительно повысить производительность.
Другие типы подсказок обычно предоставляются с синтаксисом #^Foo bar
(^Foo bar
делает то же самое в 1.2); если вы хотите добавить их в код, сгенерированный макросом, исследуйте функцию with-meta
(вам нужно объединить '{:tag Foo}
в метаданные символов, представляющих формальные аргументы для ваших функций или let
введенных вами локальных объектов, которые вы хочу поставить подсказки типа).
О, и если вы все еще хотите знать, как реализовать свою оригинальную идею ...
Вы всегда можете создать выражение Clojure, чтобы определить свою функцию - (list 'fn ['x] (a-magic-function-to-generate-some-code some-args ...))
- и вызвать eval
для результата. Это позволило бы вам сделать что-то вроде следующего (было бы проще потребовать, чтобы спецификация включала список параметров, но вот версия, предполагающая, что аргументы должны быть вычеркнуты из спецификации, все они называются paramFOO
и должны быть лексикографически отсортировано):
(require '[clojure.walk :as walk])
(defn compile-spec [spec]
(let [params (atom #{})]
(walk/prewalk
(fn [item]
(if (and (symbol? item) (.startsWith (name item) "param"))
(do (swap! params conj item)
item)
item))
spec)
(eval `(fn [~@(sort @params)] ~@spec))))
(def my-spec '[(+ (* 31 param0) 47)])
((compile-spec my-spec) 5)
; => 202
В подавляющем большинстве случаев нет веских оснований для таких действий, и этого следует избегать; используйте функции высшего порядка и макросы вместо этого. Однако, если вы делаете что-то вроде, скажем, эволюционного программирования, то оно есть, обеспечивая максимальную гибкость - и результат по-прежнему остается скомпилированной функцией.