静态分辨的类型参数

Moh*_*adi 5 parameters f# types

以下(简化)代码段取自我正在实现的应用程序,该应用程序始终使用静态解析的类型参数.

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 () = ()
Run Code Online (Sandbox Code Playgroud)

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

编辑.单独声明它们是有效的,但如果我有一个共同引用,我不能声明第三种类型放在顶部,其中包含两种类型的公共信息.我该怎么做才能避免这个问题?无论如何,如果我在下面定义let pluto = ("" :> obj) :?> A< BTy >它的定义,我想是因为这两种类型都是从let绑定中可见的.

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

无论如何,我认为在类型声明中使用静态成员约束有点复杂.更简洁的方法是定义一个清晰描述(和文档)所需成员的界面:

type IMyMember =
  abstract MyMember : unit -> unit
Run Code Online (Sandbox Code Playgroud)

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

/// 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) ()) }
Run Code Online (Sandbox Code Playgroud)

例如,该函数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> }
Run Code Online (Sandbox Code Playgroud)

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


Moh*_*adi 0

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

\n\n
type Operations< ^a> = \n  {  \n    MyMember : unit -> unit\n  }\n\ntype A< ^a> = {\n  Operations : Operations< ^a>\n}\n\nand TestA = { \n  AField : A< BTy > \n}\n\nand BTy = { \n  BField : Unit \n}\n\nlet it = \n  let BTy_operations : Operations< BTy > = { MyMember = fun () -> () }\n  let A_of_BTy = { Operations = BTy_operations }\n  { AField = A_of_BTy }\n
Run Code Online (Sandbox Code Playgroud)\n

  • 但请记住,**F# 不是 Haskell**。F# 是一种多范式语言,它旨在允许在有意义的情况下将面向对象的概念(例如接口)与函数概念(例如记录)混合在一起。F# 不支持 Haskell 风格的类型类,因此人们使用不同的编程模式来解决在 Haskell 中使用类型类解决的问题。尝试模仿 Haskell 中有效的解决方案不会为您提供惯用的 F# 代码。 (6认同)