Подтвердить что ты не робот

Почему нет деструкции в форме 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?

4b9b3361

Ответ 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) дополнительные переменные, вероятно, приемлемы.