c#函数式编程 Functional Programming in C# [11]

设计函数签名和类型

本章涵盖

  • 精心设计的函数签名
  • 对函数输入的细粒度控制
  • 使用 Option 表示可能缺少数据

  到目前为止,我们介绍的原则定义了一般的函数式编程,无论您是使用静态类型语言(如 C#)还是动态类型语言(如 JavaScript)进行编程。在本章中,您将学习一些特定于静态类型语言的函数式技术:因为函数和它们的参数都是类型化的,这就打开了一整套有趣的考虑。
  函数是函数式程序的构件,因此,正确掌握函数的签名是最重要的。因为函数签名是根据其输入和输出的类型来定义的,所以正确地掌握这些类型也是很重要的。 类型设计和函数签名设计实际上是同一枚硬币的两面。
  您可能认为,在定义类和接口多年之后,您知道如何设计类型和函数。但事实证明,FP 带来了许多有趣的概念,可以帮助您提高程序的健壮性和 API 的可用性。

3.1 函数签名设计

  当您编写更多函数式代码时,您会发现自己会更频繁地查看函数签名。定义函数签名将是您开发过程中的一个重要步骤,通常是您在处理问题时所做的第一件事。
  如果我们要谈论函数签名,我们就需要一些符号,所以我将首先介绍一种在FP社区中标准的函数符号,我们将在本书中使用它。

3.1.1 箭头符号

  用于表示函数签名的箭头符号与 Haskell 和 F#等语言中使用的符号非常相似。假设我们有一个从 int 到 string 的函数 f;也就是说,它接受一个 int 作为输入并产生一个字符串作为输出。我们会像这样标记签名:

f : int -> string

  在英语中,您可以将其读作“f has type of int to string”或“f 接受一个 int 并产生一个字符串”。在 C# 中,具有此签名的函数可分配给 Func<int,string>。
  你可能会同意,箭头符号比C#类型更易读,这就是为什么我们在讨论签名时要使用它。当我们没有输入或没有输出(void)时,我们会用()来表示。
  让我们看一些例子。表 3.1 显示了以箭头符号表示的函数类型与相应的 C# 委托类型,以及具有给定签名的函数的示例实现,以 lambda 符号表示。

表 3.1 用箭头符号表示函数签名

在这里插入图片描述

  表 3.1 中的最后一个示例显示了多个输入参数:我们只是用括号将它们分组(括号用于表示元组;也就是说,我们将二元函数表示为输入参数是二元元组的一元函数)。
  现在让我们继续讨论更复杂的签名,即 HOF 的签名。让我们从下面的方法(第 1 章)开始,它接受一个字符串和一个从 IDbConnection 到 R 的函数并返回一个 R:

publicstatic R Connect<R>(string connStr, Func<IDbConnection, R> func) => 
	Using(new SqlConnection(connStr), 
	conn => { conn.Open(); return func(conn); });

  你会如何标注这个签名?第二个参数本身就是一个函数,所以它可以记为 IDbConnectionR。 HOF 的签名将注明如下:

(string, (IDbConnection -> R)) -> R

这是相应的 C# 类型:

Func<string, Func<IDbConnection, R>, R>

  箭头语法稍微更轻量级,并且更具可读性,尤其是随着签名的复杂性增加。学习它有很大的好处,因为你会在关于 FP 的书籍、文章和博客中找到它:它是来自不同语言的函数式程序员使用的通用语言。

3.1.2 签名的信息量如何?

  一些函数签名比其他函数签名更具表现力,我的意思是它们为我们提供了更多关于函数正在做什么、允许哪些输入以及我们可以预期哪些输出的信息。例如,signature() -> () 根本没有给我们任何信息:它可能会打印一些文本、增加一个计数器、发射一艘宇宙飞船……谁知道呢!另一方面,考虑这个签名:

(IEnumerable<T>, (T -> bool)) -> IEnumerable<T>

  花点时间,看看你是否能猜到这个签名的函数是做什么的。当然,在没有看到实际实现的情况下,你是无法确定的,但你可以做一个有根据的猜测。 该函数返回一个T的列表作为输入;它也接受一个T的列表,以及第二个参数,即一个从T到bool的函数:对T的谓词
  假设该函数将使用 T 上的谓词以某种方式过滤列表中的元素是合理的。简而言之,它是一个过滤功能。确实,这正是 Enumerable.Where 的签名。
  我们再看一个例子:

(IEnumerable< A >, IEnumerable< B >, ((A, B) -> C)) -> IEnumerable< C >

  你猜这个函数是做什么的?它返回一个 C 的序列,并采用 A 的序列、B 的序列以及一个从 A 和 B 计算 C 的函数。假设这个函数将计算应用于两个输入序列的元素,返回一个带有计算结果的第三个序列。这个函数可以是 Enumerable.Zip 函数,我们在第 2 章中讨论过。
  最后两个签名非常具有表现力,您可以很好地猜测实现,这当然是一个理想的特性。当您编写 API 时,您希望它是清晰的,如果签名与表达函数意图的良好命名相辅相成,那就更好了。
  当然,函数签名可以表达的数量是有限的。例如,Enumerable.TakeWhile 是一个遍历给定序列并产生所有元素的函数,只要给定谓词的计算结果为真,就具有与 Enumerable.Where 相同的签名。这是有道理的,因为 TakeWhile 也可以被视为一种过滤功能,但其工作方式与 Where 不同。
  总之,有些签名比其他签名更具表现力。在您开发 API 时,让您的签名尽可能具有表现力——这将有助于您的 API 的使用并为您的程序增加健壮性。我们将通过几个例子来说明原因,我们将在本章继续进行。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值