65.9K
CodeProject 正在变化。 阅读更多。
Home

什么是 F#?

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (17投票s)

2014 年 3 月 10 日

CPOL

9分钟阅读

viewsIcon

60448

F# 能够比 C# 更准确地描述业务问题,使其成为服务器端应用程序的理想选择。

引言

F# 是一种现代化的函数式编程语言,面向 .NET 平台,由微软研究院的一个团队开发。自 2005 年以来一直在开发中,截至 2013 年 12 月已推出 3.1 版本。它最初是一个学术研究项目,经过多年的发展,已成为一个生产就绪的语言,被许多商业公司使用,尤其是在金融领域。

开发一门与 C# 有很大不同的新语言的原因在于:

  • 从面向对象范式转向函数式范式,后者更适合进行计算和数据操作;
  • 利用 C# 因其不同理念而未包含的有价值的编程概念;
  • 避免 C# 中众所周知的、为时已晚而无法修复的固有问题。

在语言特性方面,F# 是 C# 的超集,因此 **C# 能做的一切 F# 也能做**。反之则不然:F# 拥有 C# 无法实现的一些功能。

F# 与其他 .NET 语言之间存在完全的互操作性。一旦 F# 代码被编译成程序集,**任何 .NET 系列语言都可以使用它**,如 C# 或 VB.NET。这意味着 F# 代码可以与其他用 C# 编写的系统部分和平共处。

F# 相对于 C# 的优势

使用 Option 类型替代 NULL 引用

C# 有一个危险的特性,允许使用 NULL 值来代替对象。这个特性是许多容易引入且难以检测的错误的根源。试图解决这个问题会导致在代码中添加额外的检查,使其可读性降低。

F# 不允许使用 NULL,而是必须使用 Option 类型。它们保证了安全性,并像 C# 中的 NULL 一样用于表示缺失的对象。

代数数据类型

C# 不完全支持 代数数据类型 (ADT),它允许通过组合简单数据结构来构建复杂数据结构。它以类和结构的形式支持积类型(product-types),但对和类型(sum-types)的支持有限,和类型仅由枚举表示。

相反,F# 完全支持 ADT,能够更准确地用数据结构来描述业务实体,从而减少数据误解的可能性,提高代码质量。基本上,在 F# 中,可以通过使某些情况无法表示来避免不希望发生的情况,也就是说,简单地不给编写不当代码留有任何机会。

转换 vs. 变异

C# 鼓励变异数据和使用状态。这意味着一旦一个对象被创建,它在其生命周期中会经历一系列修改,改变其状态。根据对象的当前状态,某些操作可能允许或不允许在其上进行。这需要进行检查以确保对象处于正确状态,然后才能安全使用它。未能做到这一点很可能会导致错误,引发意外行为和崩溃。

另一方面,F# 鼓励使用转换而不是变异。转换不会修改对象,因此不会改变状态。它只是创建一个新对象并将其传递下去,保持原始对象不变。这意味着该对象始终处于创建时赋予的单一状态。只处理一种状态大大简化了开发过程并减少了代码量,因为每当我们看到一个对象时,就知道它处于唯一可能的状态,并且可以安全地使用它而无需进行额外的检查。

表达式 vs. 语句

由于 C# 依赖于状态和变异,因此这些变异应用的顺序变得非常重要,因为交换两个语句的位置可能会引入错误。所以 C# 代码中的语句顺序是另一个需要注意的问题。相反,F# 中的程序本质上是一个大表达式(一个公式),由映射输入值到某些输出值的小表达式组成。表达式不同部分的计算顺序并不重要,因为在无副作用的计算模型中,计算顺序不会改变结果。因此,从使用语句转向使用表达式编程可以得到更安全的代码,摆脱由错误语句顺序引起的 bug。

将数据和逻辑分开

C# 鼓励通过允许在同一类或结构中声明属性和方法来混合数据和逻辑。但当涉及到序列化时,附加到类的逻辑必须被剥离,这就是为什么类必须被转换为不带逻辑的纯数据对象,然后才能传递给序列化器。
函数式编程建议不要混合数据和逻辑;因此,序列化到 JSON 或 XML 会更加简单直接。

类型推断和简洁语法

C# 语法冗长。与用 F# 编写相同的内容相比,用 C# 表达事物需要显著更多的输入。C# 的冗长主要来自于必须指定方法参数的类型及其返回值。F# 具有高级类型推断系统,可以从值的用法推断出其类型,因此在大多数情况下,建议在代码中省略类型,让 F# 来推断。输入更少可以提高生产力,使代码更易于维护。有些人认为 F# 的语法更优雅、更易于阅读。

从上到下、从左到右的求值顺序

C# 中文件、类和方法的声明顺序无关紧要。另一方面,在 F# 中,声明顺序严格遵循文件顺序,因此你不能引用或使用尚未声明的内容。这似乎是一个不必要的复杂性,但实际上是一件好事,因为它对开发者施加了更多的纪律,并使他们的意图更加明确。

固有的可测试性和良好的设计

被称为 SOLID 的现代面向对象设计原则在 C# 中很容易被违反;因此,了解并严格遵循它们非常重要。然而,这些原则本身就是函数式编程的本质。因此,F# 语言鼓励你从一开始就以正确的方式编写代码,而在 F# 中弄乱代码需要付出一些刻意的努力。

低成本地并行运行

由于可能发生两个线程同时尝试修改同一对象的竞争条件,并行运行 C# 代码是一项具有挑战性的任务。F# 通过不允许在创建后修改对象来完全消除这个问题,每当需要更改时,都会通过对原始对象应用转换来构造一个新对象。这意味着用 F# 编写的代码可以自然地并行运行,只需很少的额外努力。

更好的模块化

F# 中逻辑的基本单元是函数,而在 C# 中是类。函数在 F# 中是第一等公民,可以作为参数传递给其他函数,也可以作为函数的返回值。编写函数比编写类更省力。这使得更细粒度的模块化和更复杂的组合场景得以实现,且成本更低。这样编写的代码更易于维护和重构。

专注于解决通用问题而非特定问题

F# 鼓励你使用泛型数据类型而不是具体类型。为泛型编写的相同算法将适用于不同的具体类型,如数字、字符串或复杂对象。为不同类型仅有一个泛型实现可以使代码更具重用性,并减少出错的可能性。

类型提供程序

F# 有一种独特的机制,可以通过一种方便统一的方式处理异构数据源,称为 类型提供程序。类型提供程序抽象了各种数据源的实现细节,确保类型安全,并公开标准化的、定义良好的接口。它们还具有按需类型发现功能,仅在需要时加载新类型。有一个 类型提供程序存储库,可以连接到各种数据源,该存储库不断被社区更新和贡献。类型提供程序通过简化不同来源数据的消费,实现信息丰富的编程。

生成解析器的标准工具

F# 有两个标准工具 FsLexFsYacc,用于基于形式语法生成解析器。这些工具以 Unix 世界流行的解析器生成实用工具 LexYacc 命名。FsLex 和 FsYacc 具有丰富的诊断功能,可以集成到构建过程中,有助于简化开发。

F# 的缺点

以下缺点仅在以惯用方式遵循函数式编程原则使用 F# 时才重要。如果你选择不遵循这些原则,这些缺点将不再存在。

陡峭的学习曲线

对于没有函数式编程经验的人来说,在 F# 中编码时适应一种新的思维方式可能会很困难。

更复杂的数据结构

使用“转换优先于变异”的方法,需要更高级的数据结构才能对其进行高效操作。例如,在 F# 中必须使用二叉树而不是哈希表,而他们在 C# 中会这样做。另一个例子是大量使用 zippers 而不是迭代器。

垃圾回收器负担较重

严格遵循“转换优先于变异”原则会产生更多需要及时释放的对象。与操作状态正在发生变化的较少数量的对象相比,这给垃圾回收器带来了更大的负担。开发人员必须找到一种平衡的方法,并明智地使用这一原则。

命名更具挑战性

F# 没有 C# 中方法所拥有的函数重载功能。因此,F# 中位于同一模块中的两个函数不能共享相同的名称,这在为它们命名时会带来独特性方面的困难。制定一致的命名约定在 F# 中是一项具有挑战性的任务。

工具不够高级

微软在为 C# 程序员打造最佳工具方面投入了大量精力。不幸的是,F# 的工具不多,这使得编码不太舒适。F# 没有基本的重构工具。

对某些类型项目的用途有限

UI 应用程序可以很好地说明这种情况,在其中使用 F# 会很不方便,因为对控件的操作意味着改变它们的状态,而这并非 F# 的设计初衷。

摘要

F# 是一种现代函数式编程语言,旨在作为 C# 和 VB 的替代品,这两者都遵循面向对象范例。F# 借鉴了它们的最佳特性,并摒弃了危险的特性。这就是为什么用 F# 编写的代码更安全、更易于维护。尽管在某些类型的项目中 F# 是一个糟糕的选择,但对于那些大量进行计算和数据操作的项目,绝对应该考虑它。F# 能够比 C# 更准确地描述业务问题,使其成为服务器端应用程序的理想选择。

资源

  1. 微软研究院的 F# 项目:http://research.microsoft.com/en-us/projects/fsharp/
  2. F# 入门:http://fsharpforfunandprofit.com/
  3. 在线编程环境:http://www.tryfsharp.org/Learn
  4. F# Power Pack:https://fsharppowerpack.codeplex.com/

© . All rights reserved.