Clojure嵌套let的最佳实践

4

在以下情况下使用Clojure嵌套let是否是一个好习惯,还是会让人感到困惑?

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

我注意到我在与外部世界交互的输入函数中经常出现这种解析/检查/验证事物的模式(在本例中是公开函数的Clojurescript库,但我也有具有相同感觉的Compojure路线)。

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

那么做的习惯方法是什么?将config名称更改为parsed-config,放入另一个函数中,或完全采用其他方式?

3个回答

10

当“rebinding”是同一种类型的事情,并且您想要清楚地表明本地绑定取代全局绑定时,我会使用这个成语。

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

这也可以防止您不小心使用全局绑定,就像我经常犯的那样。


3
@Thumbnail的回答非常好,但我个人几乎从未以这种方式使用内部绑定来遮盖外部绑定。即使您了解绑定规则,并且想要因为某种很好的原因来遮盖外部变量,这对于阅读代码的人来说也是令人困惑的——可能很快就会是您自己,在您忘记代码如何运作之后。

假设我有一个复杂的函数,并且我在其中间看到变量foo被使用。 我向上查看并看到它的绑定--可能是作为函数参数,这是显而易见且容易注意到的。 如果我没有注意到,在这之下,名称已被重新绑定,那么我将误解变量中的内容。

因此,我通常会编写新的、相关联的名称,以便对应代码中不同变量的角色。 有时候,名称的区别有些任意。

我认为这些都是不遮盖变量的好理由,而且我认为@Thumbnail提供了好的理由来遮盖它们。 这是一种权衡,您必须决定哪种情况最适合您。

短小的函数可能更适合遮盖。 就我个人而言,如果我这样做了,我会添加一个非常显眼的注释,或者如果我一遍又一遍地这样做,也许文件顶部会有一个非常显眼的注释。

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


谢谢您的输入。那么在您的意见中,如果这种事情总是出现在函数的顶部(就像我的例子中一样),这样做是否可以呢? - nha
1
是的,既然你提到了,那听起来完全合理。我对自己实践的解释为其提供了合理性! - Mars

2

另一个选项是略微更改参数的名称,保留“最终”数据的一般名称:

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

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


这显然是一种选择,我想知道你在使用这种约定时发现了什么优缺点? - nha
1
思路是将“非常规”版本添加后缀,这使得它变长并且有点难以输入。通常情况下,“config-in”(或“config-raw”等)仅使用一次,然后立即清理并保存为“config”,这是“正常”版本,并且可以多次使用。 - Alan Thompson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接