Почему бы не разрушать в форме def? - PullRequest
15 голосов
/ 20 ноября 2011

В форме let (Clojure здесь) я могу сделать что-то вроде

(let [[u s v] (svd A)] 
   (do-something-with u v))

где svd возвращает список длины три. Это очень естественный способ сделать, так почему бы и нет, у нас нет

(def [u s v] (svd A))

и его различные обобщения как стандартное поведение формы def? Я не понимаю, как это могло бы помешать чему-либо, что def уже делает. Может кто-то, кто понимает дзен Лисп или Clojure, объяснить, почему def не поддерживает связывание (с деструктуризацией), столь же мощное, как let?

Ответы [ 4 ]

16 голосов
/ 20 ноября 2011

def - это специальная форма на уровне компилятора: она создает Var. def должен быть доступен и использоваться до того, как станет возможной деструктура. Вы видите нечто похожее с let*, примитивом компилятора, который не поддерживает деструктуризацию: затем после нескольких тысяч строк в clojure/core.clj язык, наконец, становится достаточно мощным, чтобы предоставить версию let с деструктуризацией, в качестве макроса поверх let*.

Если хотите, вы можете написать макрос (скажем, def+), который сделает это за вас. Лично я думаю, что это довольно грубо и не будет его использовать, но использование Lisp означает использование языка, который подходит вам лично.

2 голосов
/ 20 ноября 2011

def в основном конструктор для Vars. Первым аргументом является символ, который называет Var. Он принимает этот символ и возвращает Var для этого символа. Разрушение изменило бы эту семантику.

Вы могли бы написать макрос, который делает это, однако.

1 голос
/ 27 февраля 2018

Ниже приведены некоторые философские обоснования.

Clojure предпочитает неизменность, а не изменчивость, и все источники изменчивости должны быть тщательно рассмотрены и названы. def создает изменяемые переменные. Таким образом, идиоматическая Clojure, так или иначе, мало использует их, а также не хотела бы, чтобы было слишком легко создавать много изменяемых переменных без заботы (например, путем разрушения). Однако let и деструктуризация аргументов функций создают неизменные привязки, поэтому Clojure упрощает создание этих привязок.

Вары, созданные def, имеют глобальную область видимости. Поэтому вы должны тщательно назвать def ed vars и держать их в числе. Разрушение def сделало бы слишком легким создание многих def без заботы. let и деструктурирование аргументов функций, с другой стороны, создает локальные привязки с лексической областью, поэтому удобство деструктурирования не вызывает загрязнения имен.

1 голос
/ 27 февраля 2018

Это не идеально, но начать писать 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 может быть решена, но с учетом варианта использования (use в repl) дополнительные переменные, вероятно, приемлемы.

...