【问题标题】:How can I leave generic type params unrealized in an interface implementation?如何在接口实现中保留未实现的泛型类型参数?
【发布时间】:2017-05-18 14:51:33
【问题描述】:

假设我有以下通用接口

type IStorageX<'a, 'b> =
    abstract Make: 'a -> 'b
    abstract From: 'c -> 'b

还有以下2种

type SomeInput<'a> = {value : 'a}

type SomeThing = {value: string}
    with interface IStorageX<int, SomeThing> with
        member this.Make x = {value = sprintf "%A" x}    
        member this.From (x:SomeInput<'a>) = {value = sprintf "%A" x.value}    

我怎样才能让 F# 理解 member From 应该允许任何类型的输入(只要它是 SomeInput)?

我在某种程度上需要尝试的是

type IStorageX<'a, 'b, 'c> =
    abstract Make: 'a -> 'b
    abstract From: 'c -> 'b

type SomeThing = {value: string}
    with interface IStorageX<int, SomeThing, SomeInput<'a>> with
                                                     //^^--- unrealized generic param here
        member this.Make x = {value = sprintf "%A" x}    
        member this.From x = {value = sprintf "%A" x.value}    

但这也无法编译,因为我可能在未实现的接口上没有泛型类型参数。

我只能这样做

type SomeThing = {value: string}
    with interface IStorageX<int, SomeThing, SomeInput<int>> with
        member this.Make x = {value = sprintf "%A" x}    
        member this.From (x:SomeInput<'a>) = {value = sprintf "%A" x.value}    

即完全制定所有通用参数。这当然行不通,因为我必须为每种类型的 SomeInput 实现它们......

你知道怎么做吗?

谢谢

PS:我能够非常快地使用 SRTP 定义解决方案。但我想要一个基于接口的解决方案,因为 SRTP 在某种程度上很糟糕,而且 F# 领域的每个人都喜欢“不,不,不!讨厌的男孩!”

【问题讨论】:

  • CLR 既不支持在实现时限制接口(因为那是荒谬的)也不支持更高种类的多态(只是因为)。如果您真的需要它来工作,那么静态解决的约束没有任何问题。但是你真的需要这个工作吗?
  • “我需要那个吗?”你的意思是“接口方法”?不,因为我已经通过 SRTP 实现了它。你的问题是:我需要一个通用的方法吗?哦,是的 ;-) 我只是想成为一个好男孩,并以传统的 F# 方式做到这一点
  • 尽管如此,我不明白的是:我已经定义了abstract From: 'c -&gt; 'b,当我用任何具体类型实现该成员时(并且没有将那个额外的类型参数放在接口的参数列表中)编译器给了我一个错误告诉我使用的类型不是'c。嗯,这是正确的,但没有类型 'c。所以问题是为什么我什至允许在其抽象成员之一中定义一个具有泛型类型参数的接口,而不是接口本身的类型参数的一部分
  • 也许您可以查看eiriktsarpalis.github.io/typeshape/# ?但不确定它会解决这里的prb...
  • 接口是对消费者的“承诺”,也是对实施者的“要求”。当您将接口方法定义为'a -&gt; 'b 时,这意味着“每个实现都必须能够接受消费者传入的任何类型'a”。您的实现不能将输入限制为比接口声明中定义的更窄的类型集,否则如果某些消费者使用“错误”类型的参数调用该方法会发生什么?

标签: generics f#


【解决方案1】:

从实施方面和消费者方面提供更多关于您真正想要完成的内容的详细信息对您非常有帮助 - 否则很难推断出最佳的惯用解决方案是什么。以您的第二个示例为基础,似乎您希望您能做的就是采取

type IStorageX<'a, 'b, 'c> =
    abstract Make: 'a -> 'b
    abstract From: 'c -> 'b

并让您的类型SomeThing 为任何'a 实现IStorageX&lt;int, SomeThing, SomeInput&lt;'a&gt;&gt;,但没有办法做到这一点 - 类型无法在.NET 类型系统中实现forall 'a.I&lt;'a&gt;。但是,您可以提供一个方法,而不是实现一个接口:

type SomeThing = 
    {value:string}
    member this.AsStorage() = { 
        new IStorageX<int,SomeThing,SomeInput<'a>> with 
            member this.Make x = {value = sprintf "%A" x}    
            member this.From (x:SomeInput<'a>) = {value = sprintf "%A" x.value}
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-29
    • 2010-11-11
    • 2020-11-12
    • 1970-01-01
    相关资源
    最近更新 更多