generics - F# 泛型类型约束和鸭子类型

标签 generics f# inline

我正在尝试在 F# 中实现鸭子输入,我发现您可以使用 member constraint in F# generics如下:

type ListEntryViewModel<'T when 'T : (member Name : string)>(model:'T) = 
  inherit ViewModelBase()

  member this.Name with get() = model.Name

但是,当我尝试引用该属性时,上面的代码将无法编译。我得到一个编译器错误:

This code is not sufficiently generic. The type variable ^T when ^T : (member get_Name : ^T -> string) could not be generalized because it would escape its scope.



是否可以通过通用约束来实现鸭子类型?

最佳答案

最近有一个类似的问题,member constraints were used in the type declaration .

我不确定如何更正您的示例以使其编译,但如果这是不可能的,我不会感到惊讶。成员约束旨在与静态解析的类型参数一起使用,尤其是与 inline 一起使用。函数或成员,我不认为将它们与类的类型参数一起使用是惯用的 F# 代码。

我认为对您的示例更惯用的解决方案是定义一个接口(interface):

type INamed = 
  abstract Name : string

type ListEntryViewModel<'T when 'T :> INamed>(model:'T) =  
  member this.Name = model.Name

(事实上​​,ListEntryViewModel 可能不需要类型参数,可以将 INamed 作为构造函数参数,但这样写可能会有一些好处。)

现在,您仍然可以使用鸭子打字并使用 ListEntryViewModel在有 Name 的东西上属性,但不实现 INamed界面!这可以通过编写 inline 来完成。返回 INamed 的函数并使用静态成员约束来捕获现有的 Name属性(property):
let inline namedModel< ^T when ^T : (member Name : string)> (model:^T)= 
  { new INamed with
      member x.Name = 
        (^T : (member Name : string) model) }

然后,您可以通过编写 ListEntryViewModel(namedModel someObj) 创建您的 View 模型。在哪里 someObj不必实现接口(interface),只需要 Name属性(property)。

我更喜欢这种风格,因为通过采用接口(interface),您可以更好地记录您对模型的要求。如果您有其他不符合该方案的对象,您可以调整它们,但如果您正在编写模型,那么实现接口(interface)是确保它公开所有必需功能的好方法。

关于generics - F# 泛型类型约束和鸭子类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13011811/

相关文章:

javascript - JavaScript 内联函数的输入参数

c# - 接口(interface)协变逆变: why is this not compiling?

java - 如何编写加法的通用方法

c - "error: ‘_Generic’ 指定了两个兼容的类型”但在某些编译器上没有

java - 有什么可以阻止 java 内联方法吗?

javascript - 为什么禁止内联脚本(内容安全策略)?

c# - 如何获取泛型参数的类型?

F# 异步问题

f# - FsUnit 不匹配异常 - F#

具有多个输入参数的 F# 函数组合