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 : A< BTy >当我定义字段 AField ( )的类型时,IntelliSense 给了我以下错误:类型 'BTy' 不支持任何名为 'MyMember' 的运算符

已编辑。分别声明它们是有效的,但是如果我有一个相互引用并且我不能声明第三种类型放在包含这两种类型的公共信息的顶部。我应该怎么做才能避免这个问题?无论如何,如果我在它的工作定义下面定义let pluto = ("" :> obj) :?> A< BTy >,我想是因为这两种类型都可以从 let 绑定中看到。

4

2 回答 2

12

老实说,我有点惊讶您甚至可以在类型声明中使用静态成员约束,但正如@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) ()) }

例如,该函数IMyMember将从您的BTy类型创建:

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

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

另外,我在一篇展示如何编写通用数字代码的文章中使用了相同的技术,我认为它在那里工作得非常好。

于 2012-10-15T00:07:17.023 回答
0

您的实现@Tomas 满足了这个问题,但对您的解决方案的风格有些犹豫,因为它不尊重函数式编程范式。我想到了一个由 Haskell 的 Type Classes 实现产生的解决方案。已经实现了接口、抽象类等,以允许 F# 环境与 .Net 环境“交互”,出于统一风格的原因,在以下上下文中使用实现接口、抽象类等的代码无需与 .Net 库进行交互,在我看来这是一个“开销”(尽管 F# 是一种多范式语言)。这就是为什么我发现 Haskell 的 Type Classes 的实现非常优雅的原因。下面我通过F#实现了“Haskell Type Class”代码来解决我的问题。

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 }
于 2012-10-15T21:53:20.830 回答