【问题标题】:Any linter to warn about side-effects in JavaScript?任何 linter 警告 JavaScript 中的副作用?
【发布时间】:2017-02-03 12:08:19
【问题描述】:

借助 JavaScript 的灵活性,我们可以编写充满副作用的代码,或者只是纯粹的函数式代码。

我一直对函数式 JavaScript 很感兴趣,并希望以这种范式开始一个项目。关于这一点的短绒肯定可以帮助我收集良好的实践。是否有任何 linter 可以强制执行纯函数式和无副作用的样式?

【问题讨论】:

  • @VinodLouis:据我所知,JSLint 没有提供任何检查功能纯度的方法。
  • 你能描述一下你所说的side-effects in JavaScript吗?
  • JS 以副作用为特色
  • @Justinas:参照透明度或纯度意味着您可以在代码中的任何地方用它的值替换表达式,而不会改变程序的含义。如果您不能 这样做,则表达式是不纯的并且不是引用透明的。或者,换句话说:函数的结果必须完全包含在它的返回值中,并且只能依赖于它的参数。 除了之外发生的任何其他副作用:I/O、变异状态、抛出异常、打印到控制台、访问网络、生成工作器、读取或写入文件或数据库。
  • @Thomas:嗯,按照你的推理,错误也是特征。

标签: javascript functional-programming eslint


【解决方案1】:

纯度分析相当于解决停机问题,因此任何可以确定代码是纯还是不纯的静态分析在一般情况下都是不可能的。总会有无数个程序,它们是否纯正是无法确定的。其中一些程序是纯的,一些是不纯的。

现在,您故意使用术语“linter”而不是静态分析器(当然,linter 只是一个静态分析器),这似乎暗示您可以接受近似启发式结果。您可以拥有一个 linter,它有时会告诉您您的代码是纯的,有时会告诉您您的代码是不纯的,并且大多数时候会告诉您它无法确定您的代码是纯的还是不纯的。并且您可以拥有一个已知纯操作的白名单(例如,使用 + 运算符添加两个 Numbers),以及一个已知不纯的操作黑名单(例如,任何可能引发异常的操作,任何类型的循环、if 语句、Array.prototype.forEach) 并对它们进行启发式扫描。

但最终,结果将太不可靠,无法对它们做任何严肃的事情。

【讨论】:

  • 为什么if 语句被认为是不纯的?因为他们不返回任何东西,因此必须执行副作用才能做一些有意义的事情?
  • 是的,就是这样。换句话说:因为它们是语句。语句根据定义是不纯的:“纯”意味着您可以在不改变程序含义的情况下将表达式替换为其值。一个语句没有价值,因此,我应该能够在不改变程序含义的情况下用任何内容替换它(即删除它)。这仅适用于 NO-OP 语句,任何实际执行任何操作的语句都不可能是纯粹的。
  • if 只有当其部分有副作用时才有副作用。您可以看到许多 if 用于决定返回哪个值。在这种情况下, if 可以被返回的值(或者更确切地说类似于三元运算符)替换
  • @jJ':在 Ruby、Scala、Smalltalk、Self、Newspeak、Io、Ioke、Seph、Lisp、Scheme、Fancy、Haskell 等语言中,if 是一个表达式,是的.但不是在 ECMAScript 中:在 ECMAScript 中,if 是一个语句,没有任何价值。时期。如果 ECMAScript 中的 if 没有副作用,那么它根本没有任何效果。条件运算符OTOH,是一个表达式,有一个值。
  • @Jörg 我认为@jJ 意味着更复杂的递归函数,其中每个递归情况和基本情况都包含在if 语句中,并在满足相应条件时执行return 语句。我不知道 Javascript 的 return 是否被认为是纯函数式语言的副作用。所以也许这样的递归函数在技术 POV 中是不纯的,但它的调用仍然应该是引用透明的。
【解决方案2】:

我自己没有用过这个,但是我找到了这个 ESLint 插件:https://github.com/jfmengels/eslint-plugin-fp

【讨论】:

    【解决方案3】:

    你不能完全使用 JS 而没有副作用。每个 DOM 访问都是一个副作用,我们可以有一个论点,即整个全局命名空间也可能属于该定义。

    你能做的最好的就是保持理智。我在逻辑上将其分为两组:

    • 工作马(实用程序):它们的目的是获取一些数据并以某种方式对其进行处理。这些(大部分) 没有副作用。主要是因为有时这些函数需要一些状态,比如计数器或缓存,这可能会被认为是一种副作用,但由于这是隔离/封闭在这些函数中,我并不关心。
      喜欢函数传递给 Array#map() 或 promises then() 以及类似的地方。

    • 和管理:这些功能很少自己做一些数据处理,它们主要协调数据流,从创建的地方到必须运行的任何处理(-实用程序),直到结束的地方,比如修改 DOM,或者改变一个对象。

      var theOnesINeed = compose(...);
      var theOtherOnesINeed = compose(...);
      var intoADifferentFormat = function(value){ return ... }
      
      function(event){
          var a = someList.filter(theOnesINeed).map(intoADifferentFormat);
          var b = someOtherList.filter(theOtherOnesINeed);
          var rows = a.concat(b).map(wrap('<li>', '</li>'));
      
          document.querySelector('#youYesYou').innerHTML = rows.join('\n');
      }
      

    使所有功能尽可能简短。并且不要害怕描述性名称(不像这些一般名称的方式:))

    【讨论】:

    • 定义什么是副作用和什么不是副作用是微不足道的。您可以只检查 Purescript 的 DOM/窗口绑定。只要原生 js/浏览器 API 被单子效果包裹,编写没有副作用的 JS 也不是不可能的。简而言之:Monad 可以对副作用进行编码,同时仍保持引用透明性。但是在 JS 中使用 monad 看起来像“回调地狱”,因为该语言缺少 do 表示法。另一方面,可以使用生成器来模仿 do 表示法。
    猜你喜欢
    • 1970-01-01
    • 2018-03-05
    • 2016-07-21
    • 2021-10-27
    • 2022-07-21
    • 1970-01-01
    • 2011-05-28
    • 2011-08-17
    • 1970-01-01
    相关资源
    最近更新 更多