如何实现可设置且可检索的状态而不会发生突变或重新分配? [英] How to implement settable and retrievable state without mutation nor reassignment?

查看:56
本文介绍了如何实现可设置且可检索的状态而不会发生突变或重新分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在编写代码时,有两个规则可以很好地遵循:

There are a couple rules that are arguably good to follow when writing code:

  • 代码更易于阅读,并可以推断何时不进行重新分配;许多短毛绒猫建议尽可能选择const.
  • 代码也更易于阅读,并能说明对象何时不发生突变.如果您在代码的一部分中定义了一个对象,那么知道您可以在其他地方自由引用该对象将很有帮助,它将完全相同.

在大多数情况下,这些规则都很好,并且可以完全遵循这两个规则.但是,在实现同时具有setter和getter功能(这是编程中非常常见的模式)的模块时,是否可以同时遵循它们?例如:

In most cases, these rules are just fine, and both of them can be followed without issue. But is it possible to follow them both when implementing a module which has both setter and getter functionality (which is a very common pattern in programming)? For example:

const module = (() => {
  // Reassignment, but no mutation:
  let savedData;
  return {
    getData: () => savedData,
    setData: (newData) => savedData = newData
  };
})();

module.setData('foo');
console.log(module.getData());

const module = (() => {
  // Mutation, but no reassignment:
  const savedData = { theKey: null };
  return {
    getData: () => savedData.theKey,
    setData: (newData) => savedData.theKey = newData
  };
})();

module.setData('foo');
console.log(module.getData());

我想不出如何在不更改或重新分配某处的情况下实现这样的事情-但应该避免这种情况.我要寻找的是可能的,还是我必须选择一个,而只是耸耸肩选一个,这仅仅是生活中的事实?

I can't think of how one could possibly implement something like this without either mutating or reassigning somewhere - but such things should be avoided. Is what I'm looking for possible, or is it simply a fact of life that I must choose one or the other, and I should just shrug and pick one?

如果我调用了带有数据的模块以最初对其进行设置,而从不再次设置其数据,则可以,但是这种方法非常不灵活,无法满足大多数有用的用例.

If I called the module with the data to set it with initially, and never set its data again, it would be possible, but this approach is very inflexible and will not fulfill most useful use-cases.

const makeDataHolder = (savedData) => {
  return {
    getData: () => savedData,
  };
};

const fooData = makeDataHolder('foo');
console.log(fooData.getData());

我并不一定要寻找一种遵循 all 功能编程原理的解决方案(尽管,如果存在的话,将会很有趣),但是这个普遍的问题可能是熟悉的给那些使用函数式编程的人.

I'm not necessarily looking for a solution that follows all the principles of functional programming (though, if one exists, it'd be interesting to see), but this general problem is probably familiar to those who use functional programming.

推荐答案

首先,您在突变和重新分配之间观察到的对偶是完全正确的.通过使用不可变结构,同样可以实现具有可变数据结构的东西,但是每次都重新分配它.

First of all, the duality you're observing between mutation and reassignment is completely correct. Implementing something with a mutable data structure is equally doable by using an immutable structure, but reassigning it every time.

现在,有一点要牢记:函数式编程无法神奇地消除对突变的需求.但是,它可以做的是将突变移动到代码库的边界,从而保持代码本身干净且无突变.

Now, there is one important point to keep in mind: functional programming cannot magically remove the need for mutation. What it can do, however, is move the mutation to the boundaries of the codebase, hence keeping the code itself clean and mutation-free.

将变异推离代码"的一个示例是使用数据库.数据库本质上是可变的,这很好.没有人会认为数据库是可变的,也没有人声称不应在函数式编程中使用数据库.只要可变性仅保留在数据库中,并且处理数据库的代码本身不包含任何突变,就可以了.如果您不想将数据保留在某些数据库存储中,而是保留在内存中(例如出于性能原因),则可以使用Redis之类的内存中存储,并且代码保持不变.同样,只要您将重新分配和变更都保留在代码库本身之外,就可以遵循函数式编程的原理.

One example of "pushing the mutation away from the code" is using a database. Databases are inherently mutable, and that's fine. Nobody has a problem with databases being mutable, and nobody claims that databases shouldn't be used in functional programming. It's OK as long as the mutability is kept in the database only, and the code dealing with the database doesn't include any mutations on its own. If you don't want to keep your data in some database storage, but in memory (e.g. for performance reasons), you can use an in-memory store like Redis and still have your code mutation-free. Again, as long as you keep the reassignment and mutation out of the codebase itself, you are adhering to the principles of functional programming.

将程序看作是某些处理单元,在边缘"上具有I/O,可以从生产者那里获取数据,并将处理后的数据带给消费者:

It helps to look at a program as some processing unit which has I/O on the "edge", which brings data from the producers and takes the processed data to the consumers:

您的程序没有理由需要自己进行任何变异.它从输入端获取一些数据,对其进行处理,然后将其发送到输出端.当然,这个过程可以交织在一起.例如,在处理输入的过程中,有时每隔一段时间会将某些内容发送到日志(即输出).关键是,所有突变都发生在边缘"(写入数据库/日志/文件系统,向其他API发送http请求等).如果您觉得程序在某个时候需要getter和setter,则需要重新考虑设计,或者需要将getter/setter功能推到外层,例如数据库(设置字段).函数编程中不允许使用,但完全可以更新数据库记录).

There's no reason why your program needs to mutate anything on its own. It takes some data from the input side, processes it and sends it to the output side. This procedure can be intertwined, of course; for example, during the processing of the input, every once in a while something gets sent to the log, which is the output. The point is, all mutation happens on the "edge" (writing to database / log / filesystem, sending http requests to other APIs, etc.). If you feel like your program needs a getter and a setter at some point, you either need to re-think your design, or you need to push the getter/setter functionality away to an outer layer, such as the database (setting a field is not permitted in functional programming, but updating a database record is completely fine).

此外,许多编程语言不强制不变性,但仍鼓励使用函数式编程风格.有时,迭代编程和使用可变数据的方法被证明比该功能更具性能.只要不变性被隐藏在程序的其余部分之外,并保持本地化到代码的特定部分,就不会造成太大的伤害.例如,Scala中的所有foldLeft内部实现(我都知道)都是可变的.但这不是问题,因为foldLeft方法的用户无法访问可变数据,因此可变性不会暴露在外部.几乎所有排序算法都一样.它们以可变的方式实现,可以就地替换数组的元素,但是这些方法的用户看不到.内部实现使用可变性的事实不会降低代码的可读性或推理性,因为它在其余代码中不可见.

Also, many programming languages don't enforce immutability, but still encourage functional programming style. Sometimes iterative way of programming and using mutable data turns out to be more performant that functional. As long as the immutability is hidden away from the rest of the program and is kept localized to that particular part of the code, it doesn't hurt that much. For example, all internal implementations of foldLeft in Scala (that I know of) are mutable. But that's not a problem, because the users of the foldLeft method have no way to access the mutable data, so the mutability is not exposed to the outside. Same goes for almost all sorting algorithms; they are implemented in mutable fashion, replacing the elements of the array in-place, but the users of these methods don't see that. The fact that the internal implementation uses mutability doesn't reduce the readability or reasoning of the code, because it's not visible in the rest of the code.

这篇关于如何实现可设置且可检索的状态而不会发生突变或重新分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆