嵌套 let 的 Clojure 最佳实践

Clojure best practice for nested let

按以下方式使用 Clojure 嵌套 let 是好的做法,还是令人困惑?

(defn a-fun [config]
  (let [config (-> config (parse) (supply-defaults))]
  ;; do something with config
  ))

我注意到我在与外部世界对话的输入函数中经常有这种 parsing/checking/validating 东西的模式(在本例中是公开 public 函数的 Clojurescript 库,但我也有Compojure路线有同样的感觉)。

是否令人困惑,因为必须了解绑定可见性的规则(不确定确切的措辞是什么)?

惯用的方法是什么?将 config 名称更改为 parsed-config,将其放在另一个函数中,完全不同?

我会在

时使用这个成语
  • 重新绑定是同一种东西
  • 您想明确本地绑定取代 全球一。

例如

(defn fact [n]
  (loop [n n, answer 1]
    (if (pos? n)
      (recur (dec n) (* answer n))
      answer)))

这也会阻止您意外使用全局绑定,而我很容易这样做。

@Thumbnail 的回答很好,但我个人几乎不会以这种方式将外部绑定与内部绑定重叠。即使您了解绑定规则,并出于充分的理由想要隐藏外部变量,这也会让阅读代码的人感到困惑——稍后,当您忘记代码的工作原理后,这很可能就是您。

假设我有一个复杂的函数,我看到变量 foo 在函数中间的某处使用。我查找并看到它的绑定——可能作为函数参数,这很明显并且很容易注意到。如果我没有注意到下面某处的名称是 rebound,那么我就会误解变量中的内容。

所以我通常会根据代码中不同变量的作用来编造新的相关名称。有时名称差异有些随意。

我认为这些是不隐藏变量的充分理由,我认为@Thumbnail 给出了继续隐藏变量的理由。需要权衡取舍,您必须决定什么最适合您的情况。

短函数可能是更好的阴影上下文。就个人而言,如果我做了这种事情,或者如果我一遍又一遍地做,我会在文件顶部添加一个非常明显的注释。

编辑:正如 nha 的评论让我意识到的那样,当新绑定紧接在前一个绑定之后时,隐藏变量可能更合理;这使得很难忽略名称正在重新定义的事实。

另一种选择是稍微重命名参数,保留 "final" 版本数据的通用名称:

(defn a-fun [config-in]
  (let [config (-> config-in (parse) (supply-defaults))]
  ;; do something with config
  ))

我有时也使用后缀-arg-orig等来区分处理的各个阶段。