F#: 函数重载

6
我的问题与这个问题有些相关 - 具有通用参数类型的函数,但我无法弄清楚如何做我想要的事情。
我想定义一个“后代函数”来包装对各种 C# 类的“Descendants”调用,如下所示:

let descendants name (xDocument:XDocument) = xDocument.Descendants name

let descendants name (xElement:XElement) = xElement.Descendants name

这种方法行不通,因为我们有一个重复定义的“descendants”。
我认为可以利用内联函数和静态解析参数来定义以下方法来实现这一点:
let inline descendants name (xml : ^x when ^x : (member Descendants : XName -> seq<XElement>)) = 
    xml.Descendants name

但是,当我尝试这样做时,出现了以下错误:

在此程序点之前基于不确定类型的对象进行查找。可能需要在此程序点之前添加类型注释以约束对象的类型。这可能允许查找得到解决。

有没有办法编写第二个函数以实现我的目标?

2个回答

14

总的来说,我认为诸如^x之类的帽子类型可能被过度使用了(至少根据在 SO 上有关它们的问题数量来判断)。这是一个强大的功能,但它真正的设计主要是用来解决通用算术问题的。我认为它们可能会使 F# 程序变得不必要复杂。

如果你只是在使用 XDocumentXElement,那么答案就很简单了,因为你可以使用它们的公共基类 XContainer,并使用其中的 Descendants 方法:

let descendants name (xml:XContainer) = xml.Descendants(name)

// Both of these will work fine
descendants (XName.Get "foo") xd
descendants (XName.Get "foo") xe

如果找不到共同的基类,那么您当然可以使用^a类型,但也可以使用普通的重载方法。在F#中这是可行的,但仅适用于对象类型成员:

type Xml =
  static member Descendants(name, x:XDocument) = x.Descendants(name)
  static member Descendants(name, x:SomeOtherClass) = x.SomeOtherDescendants(name)

// The usage looks like this:
Xml.Descendants(XName.Get "foo", xd)
Xml.Descendants(XName.Get "foo", new SomeOtherClass())

(既然你引用了一个已经展示了成员函数重载是可行的答案,那么这对你来说可能并不新鲜。但对于未来会在此处查找答案的其他人来说可能很有用)。


啊,我没有意识到重载只适用于成员,显然没有仔细阅读另一篇帖子!同时也没有注意到它们都继承自XContainer,谢谢,这是一个更好的解决方案。 - user272730
我实际上尝试了普通重载,但编译器说有两个成员叫做“Descendants”并且参数数量相同。 - user272730
在一些早期版本中,F# 编译器在重载时需要使用 [<OverloadID("SomeName")>],但我不确定从哪个版本开始就已经删除了... 我只是在 F# RC 中尝试了更简单的示例(两种方法,每种方法都有两个参数),并且它可以正常工作(无需 OverloadID)。 - Tomas Petricek

4

以下代码可以编译(并且提示了调用静态成员约束函数所需的语法),请参考:

open System.Xml.Linq

let descendants1 name (xDocument:XDocument) = xDocument.Descendants name

let descendants2 name (xElement:XElement) = xElement.Descendants name

let inline descendants name (xml : ^x when ^x : (member Descendants : XName -> seq<XElement>)) =  
    (^x : (member Descendants : XName -> seq<XElement>) (xml,name))

let xd = XDocument.Load("http://www.somexml.com")
let ds = descendants (XName.op_Implicit "foo") xd
let xe = XElement.Load("http://www.somexml.com")
let eds = descendants (XName.op_Implicit "foo") xe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接