初探F# 4.0
尽管近期所有的新闻都在关注 C# 和 Windows 10,但F#也没有坐以待毙。随着 Visual Studio 2015 RC 的面世,F# 4.0 也一同浮出水面。
首先要注意的第一点就是,这是一个由社区的努力所推出的项目。在全部 38 位贡献者中,只有 4 分之 1 的人与微软有所关联。所有的工作都是在F#的 GitHub 网站上公开完成的,他们也希望能够通过这个平台获得用户的反馈。
这个新的发布版本对语言本身和运行时都带来了大量的变更,也对 IDE 进行了一些改进。你可以在F#博客上找到完整的变更列表,我们在这里将着重分析几个重点特性。
元编程的支持
自从 .NET 4.0 中引入了 LINQ 之后(译注:此处原文有误,LINQ 首次是在 .NET 3.5 中出现的),通过表达式树在 .NET 中实现元编程就成为了一种非常重要的特性。而在F# 4.0 中,编写表达式树变得前所未有的简单。
如果你为某个类型为 FSharp.Quotations.Expr 的参数加上 ReflectedDefinition 这个属性,那么调用方会自动地切换到按名称调用的方式。在之前的版本中,你必须按照下面的代码中的前两个表达式的方式对调用方进行标注,而现在只需按照第 3 个表达式那样写就可以了:
Test.Expression1 ( <@ x + 1 @> ) //typed expression
Test.Expression2 ( <@@ x + 1 @@> ) //untyped expression
Test.Expression3 ( x + 1 ) //typed expression with ReflectedDefinition attribute
消除了明确地对表达式进行引用的负担之后,那些应用了元编程技术的类库就变得容易使用多了。
改进的预处理器指令
不管你相信与否,直至目前为止,F#对预处理器指令的支持少得可怜。类似于“#if TRACE DEBUG”这样的布尔操作直到这个版本才刚刚实现。对于F# 3 以及更早的版本来说,一种临时方案是使用嵌套的#if 语句以模拟“and”表达式,并用重复性的代码模拟“or”表达式。
度量单位
在进行科学应用或工程应用时,经常会因为单位的差错而导致错误。举例来说,你可能会混淆英制单位和公制单位这两种不同的度量值。在 1999 年,正是因为这个错误导致了造价达 1 亿 2 千 5 百万美元的航天探测器 —— 火星气候探测器(Mars Orbiter)的毁灭。
F#通过某种被称为度量单位的概念消除了这种类型的 bug 的产生。将某个标量数值加上“<cm>”或“<miles/hour>”这样的前缀,就可以将其转换为单位度量。正如下面一行代码所示,在单位之间进行的每种转换都是由度量单位所表达的。
let cmPerInch : float<cm/inch> = 2.54<cm/inch>
F# 4 中的新功能之一是能够在度量单位表达式中使用分数指数。举例如下:
[<Measure>] type Jones = cm Hz^(1/2) / W
从具有多个泛型接口的类型继承
如果你不熟悉F#的话,要理解这一点有些困难,而如果你熟悉F#,你就知道这一点多么令人头疼了。在开始之前,首先想象一个表示 16 进制数字的类。在 C# 中,你在设计这个类时或许会决定让它能够与字符串和整数进行比较。
public class Hexidecimal : IComparable<string>, IComparable<int>
由于F#中类型推断的复杂性,在之前的版本中无法表达这个类。一方面,你无法定义一个具有多个接口,并且这些接口的唯一区别只在于它们的类型参数的这种类型。另一方面,你也不能够继承这样的类型。
F# 4 也没有完全解决这个问题,但它提供了某种临时方案。你现在要创建两个类,让每个类实现一个接口,并让第二个类继承于第一个类。这种代码有些繁琐,但如果你正好使用了某个基于 C# 编写的类库,那么在某些情况下必须使用这种方法。
在对象初始化器中使用扩展属性
扩展属性这一特性是 C# 使用者非常渴望得到的特性,而F#已经具备这一特性了。在这个最新版本中,能够在对象的初始化器中使用扩展属性了。
去除了微软的品牌标志
F#这门语言的专属命名空间总是以“Microsoft.FSharp”开头的,这种传统从 Visual Basic 7 就开始了。但随着F#逐渐从微软自有变成由社区驱动的项目,这种方式也显得不那么恰当了,而它身上的微软标记也在逐渐地淡化。
在这种情况下,为了保持F#代码不依赖于任何提供商与平台,在引用 FSharp.Core 运行时的命名空间、模块和类型的时候,可以选择忽略“Microsoft.”这一命名空间前缀。
性能改进:非结构化的比较
在默认的情况下,F#使用的是结构化的比较方式,而不是类型内置的操作符,例如 op_Equality 等。这种方式虽然能够简化复杂数据的比较,但也对性能造成了损害。
如果你希望选择注重内置比较操作符的性能或是语义,现在你可以使用“打开 NonStructuralComparison”这一操作改变例如=等操作符的工作方式了。在对某个循环中的 DateTime 对象的比较进行的基础测试中,其结果显示性能提高了一个数量级。
脚本调试
在 VS 2015 之前,F#开发者只能选择使用F#的互动模式,或是选择完整地访问调试器。有了新版本中的脚本调试特性,你可以右键单击某段F#脚本,并在调试器中运行它,这种方式结合了两种模式的优点。
智能地进行重编译
在 VS 2013 和之前的版本中,Visual Studio 无法检测出某个F#项目是否需要进行编译。因此,即使某个F#中没有任何变化,也必须进行重新编译。而在 VS 2015 中能够检测到某个F#项目是否保持最新,因此开发者无需等待项目进行无意义的编译了。
其它的新特性还包括改进的智能提示、针对 Option<T>的互操作 API、对于 WebClient 的异步工作流扩展等等。