超越F#基础——工作流

超越F#基础——工作流

文/Robert Pickering  译/朱永光  出处/InfoQ

这篇文章是基于我之前为InfoQ撰写的名为《 超越F#基础——活动模式》的文章,介绍了另外一个新的语言特性——工作流。在F#的介绍性书籍,《F#基础》(2007 五月,由Apress出版)的11章中介绍了纵向语言编程方式——一种使用领域特定语言(Domain Specific Language,DSL)编写程序的技术。这些DSL也许通过使用文本或XML代表具体代码的方式,来被独立实现。通常,DSL应该通过某种数据结构和表达DSL的语言的其他特性,被完全地嵌入到通用编程语言的内部。工作流是建立在F#现有的强大纵向语言编程特性的基础上的一个特性。工作流允许你捕获一小段代码来检查其中的内容,这样的特点使函数库实现者有机会把工作流用于实现DSL的程序模块——一种类似于C# 3.0中的表达式树的技术。在这篇文章中,我们将深入研究一下F#中的工作流的工作原理。
相关厂商内容 使用ASP.NET控件封装Silverlight
移动数据库SQL Anywhere 10使用案例集锦
《IDC:SOA中国路线图》技术分析报告免费下载
SOA最佳实践之深入浅出SOA域模型
SOA最佳实践之BPEL简明手册

F#是一个针对.NET框架的静态类型化函数式编程语言。它具有OCaml常见的核心语言功能,以及其他流行的函数式编程语言的一些特性,并从很多其他编程语言获取了一些思想,包括Haskell、Erlang和C#。简而言之,这意味着F#是一个具有优雅语法的编程语言,当我们能交互式地执行代码的时候感觉有点像脚本编程,但是它都是类型安全且有着良好性能的编译语言。这篇文章不是F#的介绍文章,不过网络上有很多资源可以让我们容易地学习F#。可以参阅在我之前文章中的侧边栏所附加的一个“F#资源”列表。

创建工作流

工作流由两部分组成:用户定义的编排工作流实例的代码和定义工作流功能的函数库组件。让我们来看一个非常简单的工作流例子:

这里,我们看到一个“script”工作流绑定到一个“num”标识符上。这个工作流由两个部分组成,一部分是包含在花括弧中的F#表达式,另外一部分是以“script”作为标识符的前缀——它可以让人们了解这个工作流的实际功能。现在让我们来看看函数基础结构是如何让这个“脚本”工作的:

这个代码稍微有点多。让我们一块一块的分开,来看看它们各自是如何与我们最初的脚本一起工作的。首先,类型定义定义了我们脚本的类型。在这里,脚本即是一个产生值的函数。我们早先定义的脚本“num”具有Script类型,这是由于它本身就是一个在执行时会生成一个整数值的函数(在这个例子中,这个整数值是42)。接下来,我们定义了一个名为“runScript”的函数用于执行脚本,其也用于定义“delay”来在脚本执行之前延迟脚本的构造过程(这样,任何脚本中包含的负面影响都会在脚本被构造和执行时解决掉)。
这个函数组件的下一个主要部分是ScriptBuilder类。它定义了一些用于处理组成我们脚本的多个表达式的方法。这些表达式将被作为值交给这些方法处理。这里,“Return”和“Let”方法处理在这个脚本中的let和return操作。更有意思的是let操作。在let操作中“printfn”函数被用于打印出参数的值——这意味着当脚本执行时,let操作中的值被打印到了控制台上。这种方式对于帮助调试非常有用。最后,是得到ScriptBuilder类的实例。在这个实例中,“Script”被用于“num”工作流中。所以当“num”脚本被执行时:
runScript num如下内容输被出到控制台上:
2
21
val it : int = 42开始的两个值的执行let操作被打印出来的值,后面的值是F#交互机制自动打印出的这个函数的执行结果以及它的类型。
在工作流中,它不仅可以处理常规的F#表达式(如let操作),正如前面所演示的,也可以新建表达式,如“let!”(发音为let bang)和“yield”,以及其他的一些操作。这样的特性为工作流提供了真正强大的能力,允许函数库实现者创建这些关键字并赋予它们新的意义。
现在,我们将扩展一下这个例子来把“let!”表达式也包含进来。假设我们不想把每个let操作的结果都打印到控制台,只想让需要的操作被打印出来,则可以通过实现"let!"操作来提供一种变通的方法,允许程序员选择哪些操作被打印。下面是通过“ScriptBuilder”类中额外的方法来实现的,用于处理“let!”操作的代码:

我们也需要对“Let”做一小点修改,删除打印到控制台的代码:

那么现在当我们定义和允许脚本的时候,我们能使用let!操作来打印特定的let操作执行结果到控制台上:

现在,只有21会被打印到控制台:
21
val it : int = 42F#编译器是如何处理工作流和最终执行的表达式之间的转换过程的呢?这个过程可以被称作“反语法糖(de-sugaring)”,我们将在下一节重点讲述它的工作原理。

理解De-sugaring

工作流中的表达式可以被转化为利用“连续传递的风格(continuation passing style)”的数据结构,这一个过程就是所谓的“反语法糖”,相对而言工作流就是一种“语法糖(syntactic sugar)”。
理解“反语法糖”这个概念的最好起点还是应该回到我们那个简单例子:

被转化成:

这种优点——不必手工键入这些结构——是显而易见的。“加糖(sugared)” 的版本比既难于理解又容易出错的不加糖版本更短更容易理解和键入。比起一个原始的表达式,以这种形式完成表达式的优点是更加的灵巧。重点要注意的是“Bind”和“Let”方法被调用了。这些方法通过传入一系列参数而被调用——let操作作为第一个参数,接着以一个等待获取值的函数作为第二个参数。这样话,就可以让我们能取巧打印出参数“p”并接着把它传递给“rest”函数继续进行计算。在“Bind”方法的定义中可以看到如下代码:
printfn "%A" p
rest p这是在F#工作流非常核心的机制。连续风格的传递过程允许开发人员在一个值即将被赋值的时候插入额外的动作。在我们的例子中,这些动作稍微有点简单,但这些动作能在等待异步计算完成后被调用,然后接着执行——这就是我们将在这个系列的下一篇文章中所要讲到的异步工作流。

<script type="text/javascript"></script> <script src="cache/hottags_forum_cache_jsonp.txt" type="text/javascript"></script>

<script type="text/javascript"></script>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值