Clojure库中的动态变量

时间:2017-11-15 14:11:05

标签: clojure

TL; DR:以下是图书馆的好模式吗?

(def ^{:dynamic true} *var*)
(defn my-fn [{:keys [var]}]
  (do-smth (or var *var*)))

-

说我想写一个情绪分析库。

get-sentiment fn中接受可选情绪标签但是提供默认标签作为动态变量是不是很好?

(def ^:dynamic *sentiment-level-labels*
  ["Very Negative" "Negative" "Neutral" "Positive" "Very Positive"])

;;...

(defn get-sentiment-scores
  "Takes text and gives back a 0 to 4 sentiment score for each sentences."
  [text]
  ;;...)

(defn get-sentiment 
  "Gives back a sentiment map with sentences scores, 
   average score, rounded score and labeled score.
   Can accepts custom sentiment level labels under :labels opt."
  [text & {:keys [labels]}]
  (let [scores (get-sentiment-scores text)
        average-score (get-average scores)
        rounded-score (Math/round average-score)
        label (get (or labels *sentiment-level-labels*) rounded-score)]
    {:scores scores
     :average-score average-score
     :rounded-score rounded-score
     :label label}))

Clojure图书馆编码标准官方页面说:

  

如果您提供隐式传递参数的接口   动态绑定(例如,sql中的db),也提供相同的接口   但是显式传递了参数。

https://dev.clojure.org/display/community/Library+Coding+Standards

在我的例子中,我只提供了一个接口但是带有opt参数。

这可以吗?有没有更好的方法来处理这个?

谢谢!

1 个答案:

答案 0 :(得分:3)

动态变量已满或存在陷阱。他们将您的API代码推向隐式环境耦合,并且经常强制您的调用代码添加许多(binding ...)子句,这首先使用动态变量来破坏简洁的目的。如果控制从一个线程传递到另一个线程,它们也会导致棘手的边缘情况。

在您的情况下,我建议您只需在params地图参数中传递标签:

(def default-sentiment-level-labels
  ["Very Negative" "Negative" "Neutral" "Positive" "Very Positive"])

(defn get-sentiment 
  "Gives back a sentiment map with sentences scores, 
   average score, rounded score and labeled score.
   Can accepts custom sentiment level labels under :labels opt."
  [text {:as params, :keys [labels] :or {labels default-sentiment-labels}}]
  ...))

请注意,地图的使用可能很有趣,因为地图对中介不透明:您可以让算法的各种组件仅从params地图中读取与其相关的键。