F#内置不变性对C#有什么好处?

时间:2010-02-03 18:04:04

标签: c# f# functional-programming immutability

  1. 我听说F#本身就支持不变性但是它不能在C#中复制呢?您从C#不可变数据中获得的F#不可变数据会得到什么?

  2. 同样在F#中,是否无法创建可变数据?一切都是不可改变的吗?

  3. 如果在应用程序中同时使用C#和F#,是否可以在C#中更改不可变的F#数据?或者您是否只需创建使用不可变F#数据的新C#类型并替换指向这些数据的引用?

7 个答案:

答案 0 :(得分:24)

  1. F#的工作方式使得处理不可变数据变得更容易,但在这方面没有什么特别的东西在C#中无法完成。但它可能无法提供简洁的语法。

  2. F#支持mutable关键字的可变数据。 F#不是纯函数式语言。它支持诸如循环和功能构造之类的命令式构造。

  3. 没有。不可变类型实际上是不可变的。 .NET类型系统在两种语言中共享。您无法更改C#中的不可变数据(当然,缺少反射和内存黑客)。

答案 1 :(得分:17)

Mehrdad已经回答了你所有的问题,但只是详细说明#1 - 不同之处在于你需要编写的代码量。例如,这里是C#中只读点的定义和示例用法:

class Point
{
    private readonly int x, y;

    public int X { get { return x; } }

    public int Y { get { return y; } }

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

var p = new Point(1, 2);

您可以使用自动属性来缩短它,但是对于类定义中的代码,不会强制执行不变性,仅适用于其客户端。

在F#中,你只需写:

type Point = { x: int; y: int }
let p = { x = 1; y = 2 }

如果你想要它是可变的:

type Point = { mutable x: int; mutable y: int }
let p = { x = 1; y = 2 }
p.x <- 3
p.y <- 4

答案 2 :(得分:12)

F#和C#在不变性方面的最大区别在于F#默认是不可变的。可以在C#中复制所有不可变的构造和集合,但它需要很多的额外工作。在F#中你只需免费获得它。

F#是一种混合语言,支持多种形式的可变性。 mutable关键字中最简单的

let value1 = 42; // immutable
let mutable value2 = value1; // mutable
value2 <- 13;  

禁止反射技巧,一旦在F#中创建数据,它就是不可变的,无论你使用它的语言如何。

答案 3 :(得分:5)

正如其他人所指出的,使用不可变数据有一些重要的好处。仅举几例:

  • 并行化 - 更容易并行化计算结果的程序并将其返回到不可变数据结构中。
  • 理解代码 - 如果使用不可变数据编写程序,一段代码唯一能做的就是返回一个结果 - 这使程序更具可读性,因为你看到了什么是函数/方法只是通过分析输入和&amp;输出

从技术上讲,您可以在C#和F#中编写相同的不可变数据结构。问题是,F#鼓励你这样做,而在C#中编写真正的不可变数据strectures有点痛苦。这意味着在编写F#代码时,您更有可能编写不可变结构(因为它更容易!)并且您将使用可变性(这也可能在F#中),只有当您真正需要它时(它更难!)

除了默认声明不可变类型之外,F#还有一些很好的功能,可以更轻松地使用它们。例如,在处理记录时:

type MyRect = { X : int; Y : int; Width : int; Height : int }
let r = { X = 0; Y = 0; Width = 200; Height = 100 }

// You'll often need to create clone with one field changed:
let rMoved1 = { X = 123; Y = r.Y; Width = r.Width; Height = r.Height }

// The same thing can be written using 'with' keyword:
let rMoved2 = { r with  X = 123 }

您也可以在C#中编写相同的内容,但是您需要使用方法WithX声明不可变数据结构,该方法返回带有修改后的X字段的克隆WithY,{ {1}}等......这是一个不完整的例子:

WithWidth

在没有编译器帮助的情况下,这是很多工作。

答案 4 :(得分:3)

我认为你是从一个太详细的功能特征点来看这个。

我认为默认情况下函数式编程语言的不变性的主要优点在于它如何影响开发人员的实际编程思维集。当突变是默认的时候,编程思维集通过改变数据来适应解决方案。当不变性是默认值时,编程思维集通过转换数据来适应解决方案。

详细阐述为什么后一种思维方式更好。在通过改变数据来开发程序时,最终会陷入精细细节,因此您的重点往往是程序如何在“小”中运行。通过转换数据来开发程序时,您不必担心许多细节(例如程序的不同变异部分如何相互作用),而您的重点往往是程序的工作方式。大'。因此,后者的思维定位会使开发人员对程序的运作方式有一个更好,更清晰的“全局”。

虽然对程序有一个很好的清晰“大局”很好,但在某些情况下,现实生活中的问题可能会超过它。由于制作新副本的成本很高,一些数据结构(数组,多维数组)不适合不可变性,所以应该总是应用一些常识,即使使用函数式语言也不能使用不变性。

编辑以回答评论:

功能语言纯粹主义者认为它会使代码变得更加丑陋。我认为自己不是纯粹主义者,所以我不这么认为;好吧,从理论角度来看也许更丑陋。

F#是一种不纯的函数式编程语言,因此它在需要时支持可变性。让你的一部分国家变得可变并不会使你的大部分国家不可改变的好处无效。

在大多数情况下,您最终会使用不同的(纯)数据结构,通常在命令式语言中使用数组。例如。元组,列表或树(或树顶上的集合/映射构建;标准库通常带有具有良好时间复杂度的实现)。

如果你认为你真的需要一个可变数组(出于性能原因),你可以使用它,所以没有什么能阻止你使用它。

答案 5 :(得分:3)

  

你对F#immutable有什么看法?   你没有从C#获得的数据   不可变数据?

F#允许您轻松地创建不可变数据的修改副本,这对于C#来说非常繁琐且耗时。考虑在F#中创建一条记录:

type Person = { FirstName: string; MiddleName: string; LastName: string }
let me = { FirstName = "Robert"; MiddleName = "Frederick"; LastName = "Pickering" }

说我想改变姓氏字段,在F#中我可以通过使用“with”关键字进行复制来实现这一点:

let me' = { me with LastName = "Townson" }

如果您想在C#中执行此操作,则必须创建将每个字段复制到新结构中的方法,并且很可能会使字段混淆。

集合还有其他合适的优势。例如,C#支持不可变的ReadOnlyCollection。但是,每次要向此集合添加新项目时,都必须复制整个集合。 F#build in list允许您只需将新项目添加到列表的末尾而不复制它,即新生成的列表实际上将共享大部分旧列表。即:

let myList = [3; 2; 1]
let myList' =  4 :: myList

创建“myList”时,整个“myList”保持不变,并且“4”被添加到它的头部以创建“myList”。

答案 6 :(得分:2)

  

您从C#不可变数据中获得的F#不可变数据会得到什么?

第一个好处是F#函数除了返回结果之外没有任何其他效果。没有更改全局数据,不依赖于可变数据,因此,只要您坚持使用不可变数据,相同的调用每次都可靠地生成相同的结果。

这第一个好处本身已经很好了,因为它使你的代码更容易理解,但的好处开始了,因为 F#函数可以安全地编写 即可。按功能组合编程是一种非常强大的模块化方式,可以使用可重复使用的部件进行编程。你可以构建非常强大的部分,可以用很多不同的方式组合在一起,这样你就可以用更少的代码编写更多的程序。

这些想法比约翰休斯的标志性文章Why Functional Programming Matters更好地解释了。约翰解释了如何消除变异能力实际上为语言增添了表达能力。阅读论文!