Методы решения распространенных ошибок макросов в Clojure - PullRequest
1 голос
/ 09 мая 2011

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

Возьмите текущую проблему, над которой я работаю, у меня есть эта модель:

{:users [:name :email :registered-on]
 :post [:title :author]}

и я хочу преобразовать его в форму:

(do (def new-form-users (cashew.core/new-form "/users/new" "Create a new Users"
         ["name" "email" "registered-on"] ("Name" "Email" "Registered-on"))) 
    (def new-form-post (cashew.core/new-form "/post/new" "Create a new Post"
         ["title" "author"] ("Title" "Author"))))

, для которой я написал этот макрос:

(defmacro gen-create-forms [model]
               `(do
                 ~@(for [[entity-kw values] model]
                        (let [entity-sym (-> entity-kw name capitalize)
                             fields (vec (map name values))]
                          `(def ~(symbol (str "new-form-" (name entity-kw))) (new-form ~(str "/" (name entity-kw) "/new") ~(str "Create a new " entity-sym) ~fields ~(map capitalize fields)))))))

Однако, когда я запускаю макрос, я получаю:

java.lang.String cannot be cast to clojure.lang.IFn
  [Thrown class java.lang.ClassCastException]

Я пытался вызвать macroexpand-1, но я получаю ту же ошибку, из-за которой у меня остается мало идей о том, как ее решить.

Это руководство, предоставленное Джоном Лоуренсом Аспденом , где он говорит: «Когда компилятор видит макрос, который является просто функцией, возвращающей некоторый код, он запускает функцию и заменяет код, которыйвозвращается в программу. "побудил меня написать макрос как функцию, которая принимает значения, которые у меня есть, и выводит результат, в который я хочу их преобразовать.

Таким образом, это работает:

(defn gen-create-forms [model]
               `(do
                 ~@(for [[entity-kw values] model]
                        (let [entity-sym (-> entity-kw name capitalize)
                             fields (vec (map name values))]
                          `(def ~(symbol (str "new-form-" (name entity-kw))) (new-form ~(str "/" (name entity-kw) "/new") ~(str "Create a new " entity-sym) ~fields ~(map capitalize fields)))))))

(gen-create-forms {:users [:name :email :registered-on]
              :post [:title :author]})
(do (def new-form-users (cashew.core/new-form "/users/new" "Create a new Users" ["name" "email" "registered-on"] ("Name" "Email" "Registered-on"))) (def new-form-post (cashew.core/new-form "/post/new" "Create a new Post" ["title" "author"] ("Title" "Author"))))

Я не уверен, правильно ли я здесь использую макросы или же макросы являются правильной стратегией для решения этой проблемы. Однако некоторые идеи о том, что вы делаете, сталкиваясь с исключениями при написании макросов, или о хороших методах их отладки, могут бытьочень признателен.

РЕДАКТИРОВАТЬ: Mikera привлекла мое внимание, что это не хороший пример, однако мой вопрос остается в силе.Итак, чтобы повторить , какие методы вы бы использовали, если бы столкнулись с исключением, если кодирование макросов в clojure?

1 Ответ

2 голосов
/ 09 мая 2011

Лично я бы не использовал для этого макрос - мой предложенный вариант будет:

  • Избегайте попыток генерировать новые имена символов в пространстве имен - это может стать грязным и сложным!
  • Вместо этого создайте единую структуру данных, называемую «новые формы», которая содержит все формы в хэш-карте. Вы можете использовать ключевые слова или строки в качестве ключей, лично я бы использовал такие ключевые слова, как ": users", так как вы уже используете этот подход для структуры данных модели.
  • Создание "новых форм" с помощью функции, которая принимает модель в качестве параметра и вызывает (cashew.core/new-form ... ) для каждой формы в модели, как требуется
  • Если вы хотите получить доступ к определенной форме, вы можете просто сделать (new-forms :users) или аналогичный, чтобы прочитать соответствующую форму из хэш-карты.
...