Noe*_*edy 9 generics f# inline
我正在尝试在F#中实现duck typing,我发现你可以在F#泛型中有一个成员约束,如下所示:
type ListEntryViewModel<'T when 'T : (member Name : string)>(model:'T) =
inherit ViewModelBase()
member this.Name with get() = model.Name
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试引用该属性时,上面的代码将无法编译.我收到编译器错误:
此代码不够通用.^ T :(成员get_Name:^ T - >字符串)时的类型变量^ T无法一般化,因为它会逃避其范围.
是否可以通过通用约束实现鸭子类型?
Tom*_*cek 23
最近有一个类似的问题,在类型声明中使用了成员约束.
我不确定如何纠正你的样本以使其编译,但如果不可能,我不会感到惊讶.成员约束被设计为与静态解析的类型参数一起使用,特别是与inline函数或成员一起使用,我不认为将它们与类的类型参数一起使用是惯用的F#代码.
我认为你的例子的更惯用的解决方案是定义一个接口:
type INamed =
abstract Name : string
type ListEntryViewModel<'T when 'T :> INamed>(model:'T) =
member this.Name = model.Name
Run Code Online (Sandbox Code Playgroud)
(实际上,ListEntryViewModel可能不需要类型参数,只能INamed作为构造函数参数,但以这种方式编写它可能会有一些好处.)
现在,你仍然可以使用duck typing并ListEntryViewModel在具有Name属性的东西上使用,但是不实现INamed接口!这可以通过编写一个inline函数来完成,该函数返回INamed并使用静态成员约束来捕获现有Name属性:
let inline namedModel< ^T when ^T : (member Name : string)> (model:^T)=
{ new INamed with
member x.Name =
(^T : (member Name : string) model) }
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过书面形式创建视图模型ListEntryViewModel(namedModel someObj),其中someObj没有实现接口,但需要只是Name财产.
我更喜欢这种风格,因为通过接口,您可以更好地记录模型所需的内容.如果您有其他对象不适合该方案,您可以调整它们,但如果您正在编写模型,那么实现接口是确保它公开所有必需功能的好方法.
是否可以通过通用约束实现鸭子类型?
不会.除了一些特殊情况,F#只实现了无法进行鸭子打字的名义打字.正如其他答案所解释的那样,惯用的"解决方案"是将接口改装到您希望已粘贴到该接口的所有类上,但是,当然,在您希望进行鸭子输入的大多数情况下,这是不切实际的.
请注意,F#中的此限制是从.NET继承的.如果你想看到一个更类似于鸭子打字的实用解决方案,请查看OCaml的结构类型多态变体和对象.
要使您的原始代码有效:
type ListEntryViewModel< ^T when ^T : (member Name : string)>(model:^T) =
inherit ViewModelBase()
member inline this.Name with get() = (^T : (member Name : string) model)
Run Code Online (Sandbox Code Playgroud)
因此,您必须将成员标记为"内联"并在成员函数中重复约束.
我同意Tomas的观点,在F#中通常首选基于接口的方法.