【问题标题】:F# generic type constraints and duck typingF# 泛型类型约束和鸭子类型
【发布时间】:2012-10-22 12:48:33
【问题描述】:

我正在尝试在 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

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

此代码不够通用。 ^T 时的类型变量 ^T : (member get_Name : ^T -> string) 无法概括,因为 它会逃脱它的范围。

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

【问题讨论】:

  • 请注意,这并不是真正的“鸭式打字”,而是结构(子)打字。

标签: generics f# inline


【解决方案1】:

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

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

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

type INamed = 
  abstract Name : string

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

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

现在,您仍然可以使用鸭子类型并在具有Name 属性的事物上使用ListEntryViewModel,但不要实现INamed 接口!这可以通过编写一个返回INamed 并使用静态成员约束来捕获现有Name 属性的inline 函数来完成:

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) 创建您的视图模型,其中someObj 不必实现接口,而只需要Name 属性。

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

【讨论】:

  • 问这个可能为时已晚,但内联方法中的对象表达式不会在每次调用内联方法时创建一个新对象吗?这可以被认为是使用内联方法作为接口适配器的缺点吗?尤其是在名称被多次访问的情况下。
【解决方案2】:

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

没有。除了一些特殊情况外,F# 只实现了无法进行鸭式打字的名义打字。正如其他答案所解释的那样,惯用的“解决方案”是将接口改装到您希望遵守该接口的所有类上,但当然,在您想要鸭式打字的大多数情况下,这是不切实际的。

请注意,F# 中的这一限制是从 .NET 继承的。如果您想查看类似于鸭子类型的更实用的解决方案,请查看 OCaml 的结构类型多态变体和对象。

【讨论】:

    【解决方案3】:

    使您的原始代码工作:

    type ListEntryViewModel< ^T when ^T : (member Name : string)>(model:^T) = 
        inherit ViewModelBase()
    
        member inline this.Name with get() = (^T : (member Name : string) model)
    

    所以你必须将成员标记为“内联”并在成员函数中重复约束。

    我同意 Tomas 的观点,即在 F# 中通常首选基于接口的方法。

    【讨论】:

      猜你喜欢
      • 2016-11-13
      • 1970-01-01
      • 1970-01-01
      • 2017-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-06
      • 1970-01-01
      相关资源
      最近更新 更多