我读了这篇文章:https://docs.microsoft.com/en-us/archive/blogs/ericlippert/foreach-vs-foreach

文章说:

第一个原因是,这样做违反了所有其他序列操作符所基于的函数式编程原则.显然,调用此方法的唯一目的是引起副作用.表达式的目的是计算值,而不是引起副作用.陈述的目的是为了引起副作用.这个东西的调用点看起来非常像一个表达式(尽管,不可否认,因为该方法是返回空值的,所以该表达式只能在"语句表达式"上下文中使用).我不适合使用一个也是唯一一个只对其副作用有用的序列运算符.

但我认为我应该执行语句(返回空值的方法),如日志(log)记录、呈现用户界面、写入物理磁盘等. 这项工作不能用‘功能’的方式来完成吗?

var list = new List<SomeClass>();
var query = list.Where(...).. some 'functional'-feeling combined function chains;
foreach (var log in query)
  Console.WriteLine(log);  // this is the 'right' way?
query.MyForEach(log => Console.WriteLine(log)); // this works, but violates functional programming principles

综上所述,根据函数式编程原理,我如何处理会产生副作用的工作?

推荐答案

正如Simon Peyton Jones曾经打趣的那样,如果你只有pure functions,运行它们唯一可观察到的影响将是更热的CPU,之后一名学生回答说,更热的CPU也是一个可观察到的副作用.

做函数式编程的每个人都很清楚,一些不纯的行为必须发生.您必须能够重新绘制屏幕、写入磁盘、通过网络发送位、接受用户输入等.

函数式编程不是关于消除不纯的操作,而是一个明确的目标,将它们最小化并控制它们发生的地点.常见的体系 struct 是functional core, imperative shell,通常是like a sandwich:从不纯净的来源收集数据,将所有数据传递给纯函数,然后对结果做一些不纯正的事情.

在OP中给出的代码草图中,您可以像这样将其拆分:您将拥有一个纯函数,其中包含如下代码:

var list = new List<SomeClass>();
var query = list.Where(/*...*/) //.. some 'functional'-feeling combined function chains;

程序的另一部分-通常是入口点-将调用纯函数并决定对其执行某些操作:

foreach (var log in query)
  Console.WriteLine(log);

在C#中,你可以像Eric Lippert建议的那样使用foreach,因为这是最常用的.

您也可以使用ForEach扩展方法.有些人会这么做,但这并没有什么不同.这样的操作本质上是不纯的,因此它不会使其更多或更少发挥作用.

我不确定我是否完全同意埃里克·利伯特的观点,但我承认这一论点.尽管如此,一个不错的ForEach扩展方法有时可以节省一行代码.

这样的行为在哈斯克尔并不是不存在,哈斯克尔有mapM_forM_个正是为了这样的目的.


从历史上看,在函数式编程中,特别是在静态类型语言中,有几次试图找出如何最好地对不纯操作建模的try 失败了,但在过go 的几十年里,general的解决方案是用monads来建模效果.

通常,您可以通过以不同的方式思考事物来以更简单的方式对事物进行建模,但如果所有其他方法都失败了,您总是可以用State monad来模拟局部州的Mutations .

如果您还需要执行I/O(而您需要执行always),Haskell提供了不透明的IO Monad.在C#中,您不会使用类似IO的代码,但它对think about what that would look like来说很有趣.

Csharp相关问答推荐

如何从Date中剥离Timezone部分以仅保留本地Date部分?(no字符串解析或操作)

System.Text.Json:反序列化返回为空数组的字典时出错

EF Core判断是否应用了AsSplitQuery()

哪个nuget包含SecurityStampValidatorOptions

EF Core在请求列表时忽略列,但在按ID获取时包含

使页面内容居中

MongoDB.NET-将数据绑定到模型类,但无法读取整数值

Mongo作为.NET中Testcontainers的副本集

方法从数据表中只 Select 一个条件?

由于POST中的应用程序/JWT,出现不支持的内容类型异常

如何注册类使用多级继承与接口

是否有必要在ASP.NET Core中注册可传递依赖项?

带有可选参数的模拟方法返回意外的不同值,具体取决于可选的默认值

将内置的OrderedEumable&Quot;类设置为内部类有什么好处?

为什么此名称不再被识别?名称不存在于当前上下文中?

为什么我的伺服电机不动,下面的代码?

ASP.NET MVC数据批注验证组复选框

C#静态抽象属性不能被子接口覆盖

删除MudRadio时,MudRadioGroup未 Select 正确的MudRadio

实例化列表时的集合表达式是什么?