为什么CoffeeScript中缺少阴影被认为是可以接受的

时间:2013-07-26 17:36:39

标签: scala functional-programming coffeescript

来自Scala,CoffeeScript中缺少阴影似乎很奇怪。我写了一个演示。

object ZekeDemo extends App {

  val filterAll = (arr: Seq[String]) => {
    val saved = ArrayBuffer[String]()
    val removed = ArrayBuffer[String]()

    val filterDo = (arr: Seq[String]) => {
      val saved = for {item <- arr if item != "do"} yield item
      val removed = for {item <- arr if item == "do"} yield item
      (saved, removed)
    }

    val filterSo = (arr: Seq[String]) => {
      val saved = for {item <- arr if item != "so"} yield item
      val removed = for {item <- arr if item == "so"} yield item
      (saved, removed)
    }

    val addRemoved = (item: String) => removedBuff += item
    val addSaved = (item: String) => savedBuff += item

    for {item <- filterDo(arr)._1} { addSaved(item)}
    for {item <- filterDo(arr)._2} { addRemoved(item)}

    for {item <- filterSo(arr)._1} { addSaved(item)}
    for {item <- filterSo(arr)._2} { addRemoved(item)}

    (saved, removed)
  }

  val song = Seq("do", "re", "mi", "fa", "so")

  val s = filterAll(song)._1
  val r = filterAll(song)._2

  println("saved: %s, removed: %s".format(s.mkString(","), r.mkString(",")))

}

现在这里是CoffeeScript中的相同程序:

filterAll = (arr) -> 

  saved = []
  removed = []

  filterDo = (arr) ->
    saved = ->
      item for item in arr when item != "do"
    removed = ->
      item for item in arr when item == "do"
    {"saved":saved(), "removed":removed()}

  filterSo = (arr) ->
    saved = ->
      item for item in arr when item != "so"
    removed = ->
      item for item in arr when item == "so"
    {"saved":saved(), "removed":removed()}

  addRemoved = (item) ->
    saved[saved.length] = item

  addSaved = (item) ->
    removed[removed.length] = item

  addRemoved item for item in filterDo(arr)["removed"]
  addSaved item for item in filterDo(arr)["saved"]

  addRemoved item for item in filterSo(arr)["removed"]
  addSaved item for item in filterSo(arr)["saved"]

  {"saved":saved, "removed":removed}

song = ["do", "re", "mi", "fa", "so"]

s = filterAll(song)["saved"]
r = filterAll(song)["removed"]

alert("saved: " + s + ", removed: " + r)

声明为数组的“已保存”将被引用了for comprehension的“saved”覆盖。更改变量名称会产生预期的输出。

这对我来说似乎很奇怪。功能语言之美的很大一部分不需要了解外部范围。如果我在另一个上下文(类|函数|文件)中编写我的“filterDo”函数,我相信我应该能够将它放到任何其他有效语法的上下文中,而不必担心它是否踩到了外部范围。

一种语言,要求开发人员知道当前作用域之外的作用域中的所有变量名称,不鼓励其开发人员进行封装。我可以通过将filterDo和filterSo移动到外部范围来修复此错误。但这会污染范围的命名空间并不必要地增加界面的表面积。

  

CoffeeScript的论点是没有特殊的阴影语法   变量是你根本不应该做这种事情。名称   你的变量清楚。因为即使允许遮蔽也是如此   两个变量有两个不同会很困惑   具有相同名称的含义,一个在内部范围内,一个在一个内部   封闭范围。

我原则上喜欢这个想法,但在实践中我不相信语言应该表现出这种难以追踪的行为,并且基于这种微妙的规则来解决。变量命名是编码风格的反映,风格选择不应改变程序的行为。

1 个答案:

答案 0 :(得分:1)

我认为在实践中,当编写小的defs和类等等并避免过多/过于通用的全局变量(即当你编写“好的代码”时)时,你不太可能遇到这种情况。你的例子似乎特别人为。以下是filterAll()函数的简单解决方案:

filterAll = (arr) ->
  saved: item for item in arr when item not in ['do', 'so']
  removed: item for item in arr when item in ['do', 'so']

我认为期望编码人员在他的脑海中记住给定范围内的变量名称是合理的 - 如果你按照现代设计标准编写小的defs,类等等,这是非常合理的。即使在你人为的例子中,一旦你理解了语言的运作,就很容易看到阴影的发生。

此外,默认情况下 - 除非您明确说明否则 - 您编译的CoffeeScript文件将自动包装在闭包中。因此,即使你写了“坏代码”并且在顶层使用一堆通用var名称,你也不会意外地踩踏其他文件的“全局”变量(考虑到闭包而不是全局变量;对文件是全局的)。 / p>

我认为在:=运算符的Interwebz上某处明确表示赋值给同名外部范围的var的建议是一个合理的建议(默认情况下编译器总是会重新声明{{1} } 在适用范围)。但是,此时这将成为CoffeeScript的一个新主要版本,因为它会破坏知识渊博的人所编写的代码,并实际利用您所看到的问题。 ;)

希望这会有所帮助。这个演示Scala应用程序以我的名字命名,这是一个意外的喜悦。我喜欢Scala和CoffeeScript!