F#5

2020-11-11 01:46:08

今天,我们很兴奋地宣布F#5正式发布。它与.NET5一起发布。我们在过去的一年里一直致力于F#5,我们很高兴能与大家分享它。

如果您在Windows上使用Visual Studio,则需要升级到最新的16.8版本。

从F#4.1到F#5,F#的主要关注点一直是对.NET Core(现在是.NET5)的巨大支持。有了F#5,我们认为这段旅程基本结束了。F#5标志着F#演变的新时代的开始,主要围绕三个方面:

我们带着大致相同的目标开始了F#5,在第一个预告片中说“F#5专注于更好的交互式和分析性编程”。这仍然是正确的,我们还添加了一些更多的正交特性,每个人都可以享受这些特性,而不管他们是如何使用F#的。

F#5是.NET SDK和Visual Studio的新默认语言版本。任何使用这两个工具集编译的新项目或现有项目都将使用F#5。

看看这个样本库,它展示了你可以用F#5做的一些事情。你可以尝试那里的每一个特性,而不是从头开始。

F#5支持使用#r";Nuget:...语法在F#脚本中引用包。以下是大多数套餐的外观:

包引用还支持对引用依赖.dll有特殊要求的包。例如,FParsec包过去要求用户手动确保在F#Interactive中引用FParsec.dll之前首先引用其依赖的FParsecCS.dll。这不再需要,您只需如下所示引用该包即可:

在Jupyter笔记本或VSCode笔记本中使用F#时,包引用是获取任何包的基础。

为了与包引用相一致,Jupyter笔记本、Interact和VSCode笔记本都完全支持F#5。

VSCode笔记本本身仍处于预览阶段,但它们已经支持相当多的功能:

我们希望您能试用一下,并就您觉得需要的内容给我们反馈。要做到这一点,请按照安装说明操作,在GitHub上提交问题时不要害羞!

字符串内插是最受欢迎的语言特性之一,也是我们在F#语言设计库中最初设计的第一个特性。多年来,这个设计经历了很多讨论,但最终李亚涛在如何最好地处理它方面取得了突破,他也提供了一个初步的实现方案。

F#内插字符串与C#或JavaScript内插字符串非常相似,因为它们允许您在字符串文字内的“洞”中编写代码。下面是一个基本的例子:

但是,F#内插字符串也允许类型内插,就像print intf函数一样,以强制内插上下文中的表达式符合特定类型。它使用相同的格式说明符。

对于更高级的用法,您可以在一个插值(从技术上讲几乎是整个程序)中编写多个表达式。也就是说,通常最好将函数定义放在内插字符串之外!

F#5的另一个被高度要求的特性是NameOf,它可以解析它所使用的符号,并在F#源代码中生成它的名字。这在各种情况下都很有用,例如日志记录,并保护您的日志记录不受源代码更改的影响。

最后一行将抛出异常,错误消息中将显示“月”。

最后增加的三项是对运算符工作方式的更改:为泛型类型参数添加了nameof<;type-参数>;形式,以及能够在模式匹配表达式中将nameof用作模式。

F#5还增加了对开放类型声明的支持。开放类型声明类似于在C#中打开一个静态类,只不过它有一些不同的语法和一些稍微不同的行为来适应F#语义。

使用Open Type声明,您可以打开任何类型以公开其中的静态内容。此外,您还可以打开F#定义的联合和记录以公开其内容。例如,如果您在一个模块中定义了一个联合,并且想要访问它的案例,但不想打开整个模块,那么这会很有用。

在对数据集执行分析工作时,切片数据类型至关重要。为此,我们在两个方面增强了F#切片以供发布,其中一个仍被认为是预览版。

在F#5之前,对内置FSharp.Core数据类型(数组、列表、字符串、2D数组、3D数组、4D数组)进行切片的行为通常不一致。有些边缘情况行为会引发异常,有些则不会。在F#5中,对于无法生成的切片,所有内置类型现在都会返回空切片:

内置的3D和4D数组类型一直支持切片,但它们不支持固定特定的索引(例如3D数组中的y维)。现在他们做到了!

如果要从数组中提取切片[|4;5|],该怎么办?现在这很简单了!

如今,计算表达式(CE)被用来建模“上下文计算”,或者用更多函数式编程友好的术语来说,就是一元计算。然而,与仅仅提供Monad语法相比,它们是一种更灵活的构造(如果愿意,您可以编码Monid,甚至可以编码显式违反每个Monad定律的计算表达式)。

F#5介绍了应用型CE,它是CE的一种形式,与您可能习惯的形式略有不同。如果每个计算都是独立的,并且它们的结果只是在结束时累积,则应用CE允许更高效的计算。当计算相互独立时,它们也可以微不足道地并行化。不过,这一好处有一个限制:不允许进行依赖于先前计算值的计算。

应用CES的工作是与G-Research合作完成的,G-Research经常为F#生态系统做出贡献。谢谢大家!

如果您是一个现在在库中公开CE的库作者,那么您需要注意一些额外的考虑事项。这些将在逐个版本的计算表达式文章中进行记录。

对于应用型CE的消费者来说,这与你已经在使用的CE并没有太大的不同。前面提到的关于独立计算的限制是要理解的关键概念。

多亏了Nino Floris的贡献,来自计算表达式(如F#Async)中捕获的异常的堆栈跟踪现在保留了更多信息。请考虑以下使用PLY库的代码:

在F#5之前,如果不在PLY库(以及任何其他可能出现这种情况的库)中解决问题,源函数就不会出现在堆栈跟踪中。现在,它显示了完整的跟踪信息:

现在,您可以在不同的泛型实例化上实现相同的接口。Lukas Rieger贡献了这一特性的初始设计和实现。

您可以通过实现接口的任何标准方法在F#中使用它:

这使您可以在期望用户能够使用默认实现时,安全地利用用现代C#编写的C#代码和.NET组件。

可空(值)类型(历史上称为可空类型)长期以来一直受到F#的支持,但传统上与它们交互有点麻烦,因为每次您想要传递一个值时,都必须构造一个可为空或可为空的SomeType&>包装器。现在,如果目标类型匹配,则编译器将隐式地将值类型转换为Nullable<;ThatValueType>;。现在可以执行以下代码:

.NET5改进了处理.ail调用的性能,F#在各种情况下(特别是在递归和异步代码中)会发出.ail调用。这对应用程序运行时性能的影响可能会因您编写的代码类型而异,因为并不是所有的.ail调用都是相等的。但是,与.NET Core3.1相比,您通常可以预期运行时性能会有所提高。

多年来,F#编译器的性能一直在稳步提高。我将通过在FSharpPlus库中编译核心项目来演示这一点。这个核心项目使用了大量的F#构造,对编译器本身起到了很好的压力测试作用。我对F#5、F#4.7和F#4.5运行了以下命令。

这些命令清理输出目录并强制msbuild串行运行(尽管无论如何它都可能是串行的,因为它正在编译单个项目),并报告在构建期间运行的所有任务的时间。下表显示了完成FSC任务(即F#编译器)所需的时间:

从F#4.5到F#4.7有了很大的进步,F#5也有了又一次大的飞跃!

你自己的结果可能会因各种因素而略有不同,但如果你自己试一试,你应该会看到相当相似的价差。同样值得注意的是,.NET工具链中的其他所有东西也都得到了改进,所以当你在.NET5中使用F#5时,不仅仅是F#编译器变得更快了。

F#5还带来了两个新的预览功能。要使用这些设置,您需要如下设置您的<;LangVersion>;preview<;/LangVersion>;:

我们决定在F#5的预览版中保留反向索引的功能。在完全发布之前,仍然有一些关于System.Range和System.Index互操作的问题需要解决。

语法为^IDX。下面是如何从列表末尾开始计算元素1值的方法:

您还可以为自己的类型定义反向索引。为此,您需要实现以下方法:

我们认为这三个增强将使F#中的数据类型切片更加方便。你认为如何?

对于库和框架作者来说,计算表达式是一个强大的功能。它们允许您定义知名成员并为您所在的域形成DSL,从而极大地提高了组件的表现力。

迭戈·埃斯梅里奥(Diego Esmerio)和瑞安·莱利(Ryan Riley)贡献了一个设计和实现,允许在计算表达式中重载自定义关键字。这一新功能允许编写如下代码:

在此更改之前,您可以按原样编写InputBuilder类型,但不能像上一个示例中那样使用它。由于允许重载、可选参数以及现在的System.ParamArray类型,因此一切都如您所期望的那样工作。

现在F#5发布了,我们将重点放在以下几个方面:

今天,F#开发库是一个相当复杂的代码库。有几个测试套件是在历史上的不同阶段创建的,其中一个特别具有挑战性,因为它不会加载到任何IDE工具中。我们觉得要求开源贡献者在这个测试套件中添加或更新测试这样的事情是不公平的,所以我们正在将其迁移到现代版本。代码库还构建了两个版本的F#编译器,称为“桌面编译器”和“CoreCLR编译器”。从技术角度来看,“CoreCLR编译器”可以用来在任何操作系统上构建任何针对任何.NET风格的F#项目。我们仍在建设这两个项目的唯一原因是我们打算偿还的技术债务。从开源贡献者的角度来看,这也应该简化事情。

我们还计划随着时间的推移对F#工具进行重大改进。我们已经开始这项工作了。第一步是将FSharp.Compiler.Service整合到我们的构建和打包基础架构中,这样任何使用此包的用户(例如VSCode中的Ionide插件)都可以简单地添加NuGet提要并获得每晚的更新。接下来,我们将致力于淘汰FSharp.Compiler.Private项目,该项目在功能上等同于FSharp.Compiler.Service,以便所有F#工具以相同的方式使用相同的功能。从那时起,我们打算与F#社区合作,最终强化F#LSP实现,使其支持所有F#编辑器工具(包括Visual Studio)。长期目标是所有F#工具都具有一致的行为和功能可用性,各个编辑器在该行为之上添加自己的“风格”和独特的功能集,以便F#程序员可以选择他们最喜欢的工具。

此外,我们还将继续投资于Visual Studio的F#工具,以提高性能并增加几个功能。其中一个功能被称为“内联提示”,你可以通过按住一个按键命令来激活它。这使您可以看到所有声明的推断类型以及应用时F#参数的名称(就像它们是命名参数一样):

这样的功能背后的意图是让您能够有效地询问编译器,它在任何时候都可以为您的代码推断出什么。我们有兴趣加入其他功能,让F#开发人员的工作效率更高。

最后,我们正在计划F#语言的下一个版本。议事日程上已经有几件事:

新的F#语言特性列表在不断演变,新的需求不断涌现,我们也相应地进行了调整。然而,我们仍然致力于F#5开始的方向。我们希望继续让F#成为一种令人愉快的交互式和分析性编程语言。你可以期待在不久的将来看到更多的美好。