F# 泛型类型约束和鸭子类型 [英] F# generic type constraints and duck typing

查看:18
本文介绍了F# 泛型类型约束和鸭子类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 F# 中实现鸭子输入,我发现你可以有一个 F#泛型中的成员约束如下:

I'm trying to implement duck typing in F# and I spotted that you can have a member constraint in F# generics as follows:

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

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

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

However, the above code won't compile when I try to reference the property. I get a compiler error:

此代码不够通用.^T 时的类型变量 ^T: (member get_Name : ^T -> string) 不能泛化,因为它会超出它的范围.

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.

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

Is it possible to implement duck typing via a generic constraint?

推荐答案

最近有一个类似的问题 在类型声明中使用了成员约束.

There was a similar question recently where member constraints were used in the type declaration.

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

I'm not sure how to correct your sample to make it compile, but I would not be surprised if that was not possible. Member constraints are designed to be used with statically resolved type parameters and especially with inline functions or members and I do not think it is idiomatic F# code to use them with type parameters of a class.

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

I think that a more idiomatic solution to your example would be to define an interface:

type INamed = 
  abstract Name : string

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

(实际上,ListEntryViewModel 可能不需要类型参数,只需将 INamed 作为构造函数参数即可,但将其写入可能会有一些好处这样.)

(In fact, the ListEntryViewModel probably does not need a type parameter and can just take INamed as a constructor parameter, but there may be some benefit in writing it in this way.)

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

Now, you can still use duck typing and use ListEntryViewModel on things that have Name property, but do not implement the INamed interface! This can be done by writing an inline function that returns INamed and uses static member constraints to capture the existing 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) 来创建您的视图模型,其中 someObj 不必实现接口,而只需要 Name 属性.

You can then create your view model by writing ListEntryViewModel(namedModel someObj) where someObj does not have to implement the interface, but needs just the Name property.

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

I would prefer this style, because by taking an interface, you can better document what you require from the model. If you have other objects that do not fit the scheme, you can adapt them, but if you're writing a model, then implementing an interface is a good way to make sure it exposes all the required functionality.

这篇关于F# 泛型类型约束和鸭子类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆