F#:重载功能

6 f#

我的问题与这个问题有些相关 - 具有通用参数类型的函数 - 但我无法弄清楚如何做我想要的事情.

我想定义一个'后代函数来包含对各种C#类的'Descendants'的调用,如下所示:

让后代名称(xDocument:XDocument)= xDocument.Descendants名称

让后代命名(xElement:XElement)= xElement.Descendants name

这种方法不起作用,因为我们有'后代'的重复定义.

我认为可以使用内联函数和静态解析参数来定义以下方法来代替:

let inline descendants name (xml : ^x when ^x : (member Descendants : XName -> seq<XElement>)) = 
    xml.Descendants name
Run Code Online (Sandbox Code Playgroud)

但是在尝试这样做时我遇到了这个错误:

根据此程序点之前的信息查找不确定类型的对象.在此程序点之前可能需要类型注释来约束对象的类型.这可以允许解析查找.

有没有办法可以写第二个功能来做我想做的事情?

Tom*_*cek 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
Run Code Online (Sandbox Code Playgroud)

如果你找不到一个公共基类,那么你当然可以使用^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())
Run Code Online (Sandbox Code Playgroud)

(因为你引用了一个问题,答案已经表明重载与成员一起工作,这对你来说可能不是什么新鲜事.但是对于将来会发现这个问题的其他人可能会有用).


Bri*_*ian 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
Run Code Online (Sandbox Code Playgroud)