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

F# 30:类型提供程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (5投票s)

2014 年 5 月 21 日

CPOL

7分钟阅读

viewsIcon

19769

F# 中的类型提供程序。

引言

这是我计划的 F# 系列文章的最后一篇。这并不意味着我将来不会写更多内容,但这将是当前这批文章的最后一篇。那么,这一篇将讲什么呢?

这一篇将讲类型提供程序。类型提供程序是一个相当复杂的概念,它们当然不适合初学者(至少在我看来是这样),所以我们将专注于使用类型提供程序,而不是如何创建它们(如果你愿意,可以理解为“站在巨人的肩膀上”)。

那么,什么是类型提供程序?

这是 MSDN 关于类型提供程序的说法

F# 类型提供程序是一种组件,可以为你的程序提供类型、属性和方法。类型提供程序是 F# 3.0 对信息丰富型编程的重要支持。信息丰富型编程的关键在于消除在处理互联网和现代企业环境中发现的各种信息源时的障碍。将信息源包含到程序中的一个重要障碍是需要将该信息表示为类型、属性和方法,以便在编程语言环境中使用。手动编写这些类型非常耗时且难以维护。一种常见的替代方法是使用代码生成器,它会将文件添加到你的项目中;然而,传统的代码生成类型与 F# 支持的探索式编程模式不兼容,因为每次调整服务引用时都必须替换生成的代码。

F# 类型提供程序提供的类型通常基于外部信息源。例如,用于 SQL 的 F# 类型提供程序将提供你直接处理任何 SQL 数据库表的类型、属性和方法。同样,用于 WSDL Web 服务的类型提供程序将提供你直接处理任何 WSDL Web 服务的类型、属性和方法。

F# 类型提供程序提供的类型、属性和方法的集合可以取决于程序代码中给定的参数。例如,类型提供程序可以根据连接字符串或服务 URL 提供不同的类型。通过这种方式,通过连接字符串或 URL 可用的信息空间直接集成到你的程序中。类型提供程序还可以确保类型组仅按需展开;也就是说,如果类型实际上被你的程序引用,它们就会展开。这允许以强类型的方式直接按需集成大规模信息空间,例如在线数据市场。

但我更喜欢 StackOverflow 上一位用户说的

假设你在现实世界中有一个任意的数据实体。在这个例子中,我们假设它是一个电子表格。我们还假设你有一种方法可以获取/推断该数据的模式/元数据——也就是说,你可以知道类型(例如,double 而不是 string)和关系(例如,此列表示“salary”)以及元数据(例如,此表是 2009 年 6 月的预算)。类型提供程序允许你编写一种“代理库”,该库了解某种类型的数据实体(例如,电子表格),并将该库用作编译器/IDE 工具链的一部分,这样你就可以编写类似的代码

mySpreadsheet.ByRowAndColumn.C4

或类似的代码,并获得 Intellisense(自动完成)和工具提示(例如,将单元格 C4 描述为 BobSalary)和静态类型(例如,将其设置为 doublestring 或任何其他类型)。本质上,这为你提供了静态类型对象的工具可用性,以及各种动态或代码生成系统的易用性优势,并在此基础上有所改进。代价是有人必须编写代理库(“类型提供程序”),但许多此类提供程序非常通用(例如,支持 OData、Excel 或 WMI 等),因此少数几个类型提供程序库就可以让世界上大量的数据在你的编程语言中可用,并提供静态类型和一流的工具支持。

其架构是一个开放的编译器,提供程序作者实现了一个小型接口,允许他们将新的名称/类型注入到编程上下文中。

很明显,类型提供程序在幕后做了大量工作来创建新类型,这一定使用了类似 Reflection.Emit 的东西,在编译阶段基于初始元数据创建新类型,这真是太棒了。

在哪里可以获得类型提供程序?

F# 3.0 附带了一些标准的类型提供程序,即以下几种,在下面的链接中展示了示例用法。但我不会介绍这些特定的类型提供程序,因为它们都依赖于外部事物(如 SQL Server),而这些事物在我这里很难在博客文章中演示,并且我想在 F# 系列中展示一些示例,让用户可以复制代码我在这里发布的。因此,如果你想尝试下面列出的类型提供程序,你需要按照链接,这些链接将带你到相关的示例。

我们将研究一些其他类型提供程序,这些程序具有更易于管理的依赖项,例如 XML 文件、CSV 文件等。

那么,你可以在哪里找到这些额外的类型提供程序?有一个 F# 数据库,其中包含许多类型提供程序,你可以 在此处 阅读更多关于它的信息,它也可以作为 NuGet 包使用,因此安装起来非常方便。当你下载这个包时,你将获得以下类型提供程序:

  • JSON 类型提供程序
  • XML 类型提供程序
  • CSV 类型提供程序
  • Worldbank 类型提供程序
  • Freebase 类型提供程序

你可能需要另一个类型提供程序,有人可能已经为你写了一个,所以在开始自己编写之前,值得进行一次快速的 Google 搜索。

如何使用这里的类型提供程序?

正如我所说,外面会有大量的类型提供程序,所以很有可能有一个可以满足你的需求。在这篇文章中,我将重点介绍我在上面提到的 F# 数据库中的 2 个类型提供程序。我不会涵盖这些类型提供程序的所有功能,因为原始作者已经有了很好的文档记录。

XML 类型提供程序

这是一个简单的例子,展示了如何使用类型提供程序来解析 XML。

open System
open FSharp.Data

//create a type provider to create the initial metadata
type Detailed = XmlProvider<"""<author><name full="true">Karl Popper</name></author>""">

[<EntryPoint>]
let main argv =

//now parse using known metadata
let info = Detailed.Parse("""<author><name full="false">Thomas Kuhn</name></author>""")
printfn "%s (full=%b)" info.Name.Value info.Name.Full

Console.ReadLine() |> ignore

//return 0 for main method
0

运行时结果如下:

image

以下是要注意的关键点:

  • 我们获得了这个数据(我们实际上没有具体类型)的 intellisense,它只是由类型提供程序推断出来的。
  • 我们可以使用已推断类型的属性。

当你停下来思考时,这真是太神奇了。

这是 XML 类型提供程序的稍微高级一点的版本,展示了如何处理多个节点和一个单独的 XML 文件。

open System
open FSharp.Data

//create a type provider to create the initial metadata
type Authors = 
  XmlProvider<"C:\Users\sacha\Desktop\ConsoleApplication1\ConsoleApplication1\Writers.xml">

[<EntryPoint>]
let main argv =

//now parse using known metadata
let authors = """
<authors topic="Philosophy of Mathematics">
<author name="Bertrand Russell" />
<author name="Ludwig Wittgenstein" born="1889" />
<author name="Alfred North Whitehead" died="1947" />
</authors> """

let topic = Authors.Parse(authors)

printfn "%s" topic.Topic
for author in topic.Authors do
printf " – %s" author.Name
author.Born |> Option.iter (printf " (%d)")
printfn ""

Console.ReadLine() |> ignore

//return 0 for main method
0

运行时输出如下:

image

CSV 类型提供程序

这是另一个 CSV 文件示例。

open System
open FSharp.Data

//create a type provider to create the initial metadata
type Stocks = CsvProvider<"C:\Users\sacha\Desktop\ConsoleApplication1\ConsoleApplication1\MSFT.csv">

[<EntryPoint>]
let main argv =

//now parse using known metadata
// Download the stock prices

let data = "Date,Open,High,Low,Close,Volume,Adj Close
2012-01-27,29.45,29.53,29.17,29.12,44187700,22.23
2012-01-26,29.61,29.70,29.40,29.13,49102800,23.50
2012-01-25,29.07,29.65,29.07,29.14,59231700,24.56
2012-01-24,29.47,29.57,29.18,29.15,51703300,25.34"

let msft = Stocks.Parse(data)

// Look at the most recent row. Note the 'Date' property
// is of type 'DateTime' and 'Open' has a type 'decimal'
let firstRow = msft.Rows |> Seq.head
let lastDate = firstRow.Date
let lastOpen = firstRow.Open

// Print the prices in the HLOC format
for row in msft.Rows do
printfn "HLOC: (%A, %A, %A, %A)" row.High row.Low row.Open row.Close

Console.ReadLine() |> ignore

//return 0 for main method
0

我还想强调数据类型的正确性,其中以下内容为 true

  • DateDateTime
  • Open 是 decimal

这可以在下面的屏幕截图中的 firstRow.Date 表达式上悬停鼠标时看到。

image

我认为类型提供程序非常神奇,并且在幕后做了很多出色的工作,我希望你们也能看到它们的价值,并探索一下,尝试使用它们。

就是这样

就像我说的,这是本系列的最后一篇文章,对我来说,这真是一段奇妙的旅程,我希望你们也喜欢。我真的很想知道,我收到了一些反馈,但如果能知道人们是否喜欢这个系列,以及它是否达到了目标,那就太好了。所以,如果你愿意给我留下评论,或者想请我喝杯虚拟啤酒,我很乐意收到。

哈哈。

总之,感谢倾听我关于 F# 的长篇大论。我现在将回到我的本职工作,那就是为 CodeProject 撰写我真正喜欢的文章,所以直到我们再次见面,再见,后会有期。祝你 F# 愉快!

© . All rights reserved.