12.4.2 自定义 F# 语言

728 篇文章 1 订阅
16 篇文章 0 订阅
 

12.4.2 自定义 F# 语言

 

    至止,我们所讨论过的序列表达式,都是用 seq 标识符表示,后面的代码块括在大括号中。然而,F# 允许创建我们自己的标识符,给代码块以特殊意义。通常,这个功能称为计算表达式(computation expressions),序列表达式是它的一个特例,它是由 F# 核心实现,并由编译器优化的。

    我们已经看到过,计算表达式可以包含标准的语言结构,比如 for 循环,还有别的结构,像 yield。在代码块之前的标识符,指出了这些构造的意义,其方式查询运算符(例如,Select 和 Where 扩展方法)完全相同的,它指定 LINQ 查询执行的操作。这意味着,我们可以创建自定义的计算表达式,用于处理选项值。我们可以用 for 结构处理选项值,但是,F# 给我们提供了更好的自定义表达式的方法。你可以在清单 12.16 中看到这些替代方法。第一个版本使用的语法类似于序列表达式;第二个版本用了更自然的方式,完成同样的工作。

 

Listing 12.16 Computation expressions for working with option values (F#)

 

// Value binding using customized 'for' primitive
option {
  for n in tryReadInt() do
    for m in tryReadInt() do
      yield n * m
}

// Value binding using special 'let!' primitive
option {
  let! n = tryReadInt()
  let! m = tryReadInt()
  return n * m
}

 

    发生在计算表达式内部的所有自定义的基元(例如,for、yield 和 let!)的行为,取决于 option 标识符,它定义了我们写的计算表达式的种类。现在,可以看到,序列表达式就是由 seq 标识符定义的特殊情况。我们将在 12.5 节看到如何定义标识符。现在,让我们看一下清单 12.16 中的两个例子。

    第一个版本与清单 12.15 中的 LINQ 查询极为相似。每一个 for 循环至少能够执行一次。当选项值包含一个值时,它将分别绑定到符号 n 或 m,并将执行循环体。开发人员只望循环处理集合,而不处理选项值,因此,结构 for 和 yield 通常只能用于处理序列。当我们创建计算表达式,处理其他类型值时,将使用后面版本的语法。第二个版本使用了不止两个计算表达式基元。第一个是 let!,它表示自定义的值绑定。

    在这两个版本中,值 n 和 m 的类型是 int。自定义的值绑定从类型 option<int> 的值中取出实际值。当从 TryReadInt 返回的值为 None时,它可能无法把这个值分配给符号。在这种情况下,整个计算表达式立即返回 None,不会执行其余的代码。表达式中的第二个非标准基元是 return。它指定如何从值构建选项值。在清单 12.16 中,我们给它 int 值,它构造的结果的类型是 option<int> 。

    我们刚才看到的概念可以被视为一种函数式的设计模式。我们可以使用 F# 的计算表达式,而不需要了解该模式的所有细节。如果想了解如何定义自己的计算表达式,了解概念和术语有关的背景知识,是非常有用的。侧边栏"计算表达式和单子"更详细地讨论的这种模式,并解释了它与 Haskell 单子的关系。

 

 

计算表达式和一元运算

 

    正如我们刚才所说的,F# 中的计算表达式是一种称为一元运算(monads)想法的实现,被证明在 Haskell 中非常有用。单子是数学中的术语,但 F# 使用了不同的名字,更好地反映了这个想法如何在 F# 语言中使用的。

    当定义计算表达式(或一元运算)时,我们总是使用泛型类型,比如 M <'a>。这通常称为一元类型(monadic type),并指定计算的含义。此类型可以给我们写的代码增加含义。例如,刚才我们看到的 option<'a>,给代码增加了返回未定义的值(None)的可能性。序列也一元运算的一种形式。类型 seq <'a> 给代码增加了能够处理多个值的能力。

    每个计算表达式(或一元运算)是由两个函数实现的,bind 和 return。bind ,使我们能够创建和组合处理一元类型值的计算。在清单 12.16 中,bind  操作是每当我们使用 let! 基元时使用的。return 用于构造一元类型的值。

    值得注意的是,序列表达式也一元运算的实例。对于序列,其绑定操作是 Seq.collect,虽然在序列表达式中,我们不用 let! 语法,而使用更舒服的 for 循环语法。清单 12.16 表明这两者密切相关。序列的 return 操作创建有单个元素的序列。在序列表达式的内部,这可以用一个更自然的 yield 基元来写。

 

    在下一节中,我们将学习可能是最简单的自定义计算。我们用 C# 和 F# 来实现,以解释一元类型是什么,以及如何看 bind 和 return 操作。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值