Почему нет деструкции в форме def?
В форме let
(Clojure здесь) я могу сделать что-то вроде
(let [[u s v] (svd A)]
(do-something-with u v))
где svd
возвращает список длиной три. Это очень естественная вещь, но почему бы и нет, мы не имеем
(def [u s v] (svd A))
и его различные обобщения как поведение по умолчанию формы def
? Я не вижу, как это может помешать чему-либо, что уже делает def
. Может ли кто-то, кто понимает Zen Lisp или Clojure, объяснить, почему def
не поддерживает привязку (с деструктурированием) настолько же мощную, как let
?
Ответы
Ответ 1
def
- это специальная форма на уровне компилятора: она создает Var. def
должен быть доступен и использоваться до того, как будет доступно деструктурирование. Вы видите нечто похожее с let*
, примитиком компилятора, который не поддерживает деструктурирование: после нескольких тысяч строк в clojure/core.clj
язык, наконец, достаточно мощный, чтобы обеспечить версию let
с деструктурированием, в качестве макроса поверх let*
.
Если вы хотите, вы можете написать макрос (скажем, def+
), который сделает это за вас. Лично я считаю, что это грубо и не будет использовать его, но использование Lisp означает использование языка, который вам подходит лично.
Ответ 2
def
- это в основном конструктор для Vars. Первый аргумент - это символ, который называет Var. Он принимает этот символ и возвращает Var для этого символа. Деструктурирование изменит эту семантику.
Вы можете написать макрос, который делает это.
Ответ 3
Ниже приводятся некоторые философские обоснования.
Clojure поддерживает неизменность над изменчивостью, и все источники изменчивости должны быть тщательно рассмотрены и названы. def
создает изменчивые вары. Идиоматический Clojure поэтому и не использует их много в любом случае, а также не хотел бы слишком легко создавать многие изменчивые вары без заботы (например, путем деструктурирования). let
и function destructuring, однако, создает неизменяемые привязки, поэтому Clojure упрощает создание этих привязок.
Вары, созданные def
имеют глобальный охват. Поэтому вы должны называть def
ed vars тщательно и содержать их немного. Destructuring def
упростит создание многих def
без забот. let
и аргумент функции деструктурирующий, с другой стороны, создают локальные, лексический-контекстные привязки, поэтому удобство деструктуризации не вызывает загрязнения окружающей среды имени.
Ответ 4
Это не идеально, но начинается запись def+
https://clojuredocs.org/clojure.core/destructure
(defmacro def+
"binding => binding-form
internalizes binding-forms as if by def."
{:added "1.9", :special-form true, :forms '[(def+ [bindings*])]}
[& bindings]
(let [bings (partition 2 (destructure bindings))]
(sequence cat
['(do)
(map (fn [[var value]] '(def ~var ~value)) bings)
[(mapv (fn [[var _]] (str var)) bings)]])))
С этим вы можете сделать...
(def+ [u s v] [1 5 9], foo "bar")
... не ставя под угрозу простоту def
...
(def+ foo "bar")
... это то, что было предложено и предложено. Это все еще вызывает проблему введения gensym переменных в глобальное пространство имен. Проблему gensym можно было бы обработать, но с учетом использования (использование в repl) дополнительные переменные, вероятно, приемлемы.