静态解析类型参数

5
下面是我正在实现的应用程序中提取的(简化的)代码片段,它始终使用静态解析的类型参数。
type A< ^B when ^B : (static member MyMember : Unit -> Unit)> = {
  Field : unit
}
type TestA = {
  AField : A< BTy >
}
and BTy = {
  BField : Unit
} with
  static member MyMember () = ()

当我定义字段AField的类型(AField : A< BTy >)时,IntelliSense会给出以下错误提示:类型“BTy”不支持任何名为“MyMember”的运算符

编辑后: 将它们分开声明可以解决问题,但是如果我有相互引用的情况,并且我不能声明第三个类型来包含这两种类型的共同信息,该怎么办才能避免这个问题呢?无论如何,如果我在定义下面加入 let pluto = ("" :> obj) :?> A< BTy >,它就可以工作了,我想是因为从let绑定中两种类型都是可见的。


这些约束条件是否允许?这可能会对互操作性造成各种问题,例如C#编译器无法实现它们。此外,从文档页面可以看到,“不能用于类型”是静态解析的限制。 - John Palmer
2
有没有理由不把它们分开声明?例如,先声明 type A,然后是 type BTy,再然后是 type TestA - Be Brave Be Like Ukraine
@JohnPalmer static 成员的限制条件在 MSDN 上被列为一个有效的示例。 - Be Brave Be Like Ukraine
1
bytebuster是正确的。递归类型声明与静态约束不兼容。按顺序放置您的声明,它会起作用。 - pad
2
@bytebuster - 但请检查静态解析页面http://msdn.microsoft.com/en-us/library/dd548046.aspx - 它说不能用于类型。 - John Palmer
2个回答

13

说实话,我有点惊讶您竟然可以在类型声明中使用静态成员约束,但正如@pad所提到的,当您按正确顺序放置声明并消除递归时,它是有效的(尽管我不确定在移动到更复杂的示例时是否会有其他限制):

type A< ^B when ^B : (static member MyMember : Unit -> Unit)> = 
  { Field : unit } 

type BTy = 
  { BField : Unit } 
  static member MyMember () = () 

type TestA = { AField : A<BTy> }
无论如何,我认为在类型声明中使用静态成员约束有些复杂。更干净的方法是定义一个接口,清楚地描述(和记录)您所需的成员:

无论如何,我认为在类型声明中使用静态成员约束有点复杂。更加简洁的做法是定义一个接口,明确描述并说明您需要的成员:

type IMyMember =
  abstract MyMember : unit -> unit

现在,静态成员约束仍然可以用于从具有所需成员但不实现接口的类型中创建接口实现。使用此技术,您应该能够以与类型上的静态成员约束完全相同的功能实现(但以更清晰的方式):

/// Captures 'IMyMember' implementation from another type using static constraints
let inline captureMyMember< ^B when ^B : (static member MyMember : Unit -> Unit)> =
  { new IMyMember with
      member x.MyMember () = 
        (^B : (static member MyMember : Unit -> Unit) ()) }

该函数将以你的BTy类型为基础创建IMyMember

/// A type that contains field and a captured implementation of 'IMyMember'
type A = 
  { Field : unit 
    Operations : IMyMember } 

let it = { Field = ()
           Operations = captureMyMember<BTy> }

此外,我在一篇文章中使用了同样的技巧,展示了如何编写通用数字代码,我认为那里它非常有效。


0

你的实现@Tomas满足了问题,但是对于你的解决方案风格存在一些犹豫,因为它没有遵循函数式编程范式。我想到了一个解决方案,它起源于Haskell的类型类实现。 接口、抽象类等已经被实现,以便让F#环境与.Net环境“交互”,为了统一风格的原因,在不需要与.Net库交互的情况下使用实现接口、抽象类等代码,这在我的看法中是一种“开销”(尽管F#是一种多范式语言)。这就是为什么我认为Haskell的类型类实现非常优雅的原因。下面我通过F#实现了“Haskell类型类”代码来解决我的问题。

type Operations< ^a> = 
  {  
    MyMember : unit -> unit
  }

type A< ^a> = {
  Operations : Operations< ^a>
}

and TestA = { 
  AField : A< BTy > 
}

and BTy = { 
  BField : Unit 
}

let it = 
  let BTy_operations : Operations< BTy > = { MyMember = fun () -> () }
  let A_of_BTy = { Operations = BTy_operations }
  { AField = A_of_BTy }

这也是一个不错的解决方案。它需要比使用静态约束捕获MyMember实现的实现(您必须在BTy_operations中填写实现),我认为这就是您问题的重点。 - Tomas Petricek
6
但请注意,F#并非Haskell。F# 是一种多范式语言,旨在允许在合适的情况下混合面向对象的概念(如接口)和函数式的概念(如记录)。F# 不支持Haskell风格的类型类,因此人们使用不同的编程模式来解决在Haskell中使用类型类解决的问题。试图模仿在Haskell中有效的解决方案将无法产生符合惯用法的 F# 代码。 - Tomas Petricek

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