Threading arrow частные определения в clojure.test

Рассмотрим следующие функции в пространстве имен MVE (минимальный жизнеспособный пример) из свежего lein new app arrow-mve, Функция extract-one является публичным, и функция extract-two это личное. Я включил main- Функция только для полноты и для удаленной возможности, что это повлекло за собой мою проблему:

(ns arrow-mve.core
  (:gen-class))

(defn extract-one [m]
  (-> m :a))

(defn- extract-two [m]
  (-> m :a))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

В моем параллельном тестовом пространстве имен я могу проверить эти функции следующим образом. Я могу проверить публичную функцию extract-one либо прямым вызовом, либо с помощью макроса с многопоточностью ->, Также обратите внимание, что у меня нет проблем со ссылкой на приватную функцию, extract-twoпо полной Var в прямом звонке. Эти тесты проходят:

(ns arrow-mve.core-test
  (:require [clojure.test :refer :all]
            [arrow-mve.core :refer :all]))

(deftest test-one-a
  (is (= 1 (extract-one {:a 1, :b 2}))))

(deftest test-one-b
  (is (= 1 (-> {:a 1, :b 2}
               extract-one))))

(deftest test-two-a
  (is (= 1 (#'arrow-mve.core/extract-two
            {:a 1, :b 2}))))

Но я получаю ошибку компиляции при попытке вызвать приватную функцию extract-two со стрелкой макрос:

(deftest test-two-b
  (is (= 1 (-> {:a 1, :b 2}
               #'arrow-mve.core/extract-two))))

$ lein test
Exception in thread "main" java.lang.RuntimeException: Unable to resolve 
  var: arrow.mve.core/extract-two in this context, compiling:
(arrow_mve/core_test.clj:10:12)
  at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
  at clojure.lang.Compiler.analyze(Compiler.java:6669)
  at clojure.lang.Compiler.analyze(Compiler.java:6625)

Все становится более странным, когда я делаю тест немного более сложным.

(deftest test-two-b
  (is (= {:x 3.14, :y 2.72}
         (-> {:a {:x 3.14, :y 2.72}, :b 2}
             #'arrow-mve.core/extract-two))))

$ lein test
Exception in thread "main" java.lang.ClassCastException: 
  clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Symbol, 
  compiling:(arrow_mve/core_test.clj:18:10)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)

Опять же, тест проходит в форме прямого вызова:

(deftest test-two-b
  (is (= {:x 3.14, :y 2.72}
         (#'arrow-mve.core/extract-two
          {:a {:x 3.14, :y 2.72}, :b 2}))))

Я подозреваю, что проблема заключается в ограничении макро-цепочки через deftest, isчитатель макроса #' за Var, и макрос стрелки, и интересно, было ли это из-за замысла или потенциальной ошибки. Конечно, в моем реальном приложении (не в этом MVE) у меня есть длинные и глубокие цепочки вызовов, которые делают использование макросов со стрелками весьма желательным.

1 ответ

Решение

Вот ответ (разные нс):

Основное пространство имен:

(ns clj.core
  (:require [tupelo.core :as t] ))
(t/refer-tupelo)

(defn extract-one [m]
  (-> m :a))

(defn- extract-two [m]
  (-> m :a))

Тестирование пространства имен:

(ns tst.clj.core
  (:use clj.core
        clojure.test )
  (:require [tupelo.core :as t]))
(t/refer-tupelo)

(deftest test-one-a
  (is (= 1 (extract-one {:a 1, :b 2}))))

(deftest test-one-b
  (is (= 1 (-> {:a 1, :b 2}
               extract-one))))

(deftest test-two-a1
  (is (= 1 (#'clj.core/extract-two {:a 1, :b 2}))))

;(deftest test-two-b
;  (is (= 1 (-> {:a 1, :b 2}
;               clj.core/extract-two))))  ; fails: not public

;(deftest test-two-b1
;  (is (= 1 (-> {:a 1, :b 2}
;               #'clj.core/extract-two))))
;     fails: can't cast PersistentArrayMap to Symbol

(deftest test-two-b
  (is (= 1 (-> {:a 1, :b 2} 
               (#'clj.core/extract-two)))))  ; works

Ответ в том, что ссылка на var должна быть в скобках. Все макросы потока имеют тест вида (псевдокод):

(if (not (list? form))
  '(form)
  form)

Так что форма как

(-> 1
    inc)

превращается в

(-> 1
    (inc))

до того, как остальная часть потока происходит. if Тест, похоже, не подходит для вас, так как переменная не является символом. Включение переменной var в список как вызов функции решает проблему.

Я предпочитаю всегда заключать вызовы функций в потоки в круглых скобках и не использовать "голые" функции, даже если это обычно допустимо:

(-> 1
    (inc)    ; could have typed "inc" w/o parens
    (* 2))   ; must use parens since more than 1 arg
;=> 4
Другие вопросы по тегам