【问题标题】:Implementing F# interface types that have methods with generic type parameters实现具有泛型类型参数的方法的 F# 接口类型
【发布时间】:2021-01-16 15:32:35
【问题描述】:

在 F# 组件设计指南的 Use interfaces to group related operations 部分中,它列出了具有泛型类型参数 'T 的方法的此接口类型。

type Serializer =
    abstract Serialize<'T> : preserveRefEq: bool -> value: 'T -> string
    abstract Deserialize<'T> : preserveRefEq: bool -> pickle: string -> 'T

object expressions 参考部分和interfaces 参考部分没有列出如何使用通用'T 参数实现接口的任何示例。 Generics 参考部分的末尾确实有一个简短的示例...我仍然难以编写反映该示例的代码。

我知道如何实现具有泛型类型的接口,而不是具有泛型类型参数的接口。

这是一个简单的玩具示例,我一直在尝试实现以了解如何做到这一点:

type Foo = Foo of string
type Bar = Bar of string

let unwrapFoo (Foo foo) = foo
let unwrapBar (Bar bar) = bar

type IWrapper =
    abstract member Unwrap<'T> : 'T -> string
    abstract member Wrap<'U> : string -> 'U

尝试实现 IWrapper 是我遇到编译器错误的地方:

let wrapper =
    { new IWrapper with
        member this.Unwrap<Foo>(foo: Foo) = unwrapFoo foo
        member this.Wrap<Bar> string = Bar string }

wrapper let 绑定会给我一个编译器错误:Unexpected identifier in pattern. Expected infix operator, quote symbol or other token.

我在实现IWrapper 的其他尝试给了我这个编译器错误:typecheck error The member 'Unwrap&lt;'T&gt; : Foo -&gt; string' does not have the correct type to override the corresponding abstract method. The required signature is 'Unwrap&lt;'T&gt; : 'T -&gt; string'.

let wrapper =
    { new IWrapper with
        member this.Unwrap<'T>(foo: Foo) = unwrapFoo foo
        member this.Wrap<'U> string = Bar(string) }

非常感谢您对实现此接口类型的任何帮助以及对阅读编译器错误消息的洞察力(我确信这是在向我解释如何解决我的问题)。谢谢大家!

【问题讨论】:

  • 您的声明应该是member this.Unwrap&lt;'T&gt;(foo: 'T) = ...,尽管您将无法调用unwrapFoo,因为foo 的类型错误。 WrapUnwrap 需要适用于所有类型,因此您不能将实现专门用于某些特定类型,例如 Foo

标签: f#


【解决方案1】:

通用签名并不意味着你认为它的意思。

这并不意味着“我将采用 some 类型作为参数。不知道那个类型是什么,我稍后再决定,但现在让我们称之为@ 987654321@。”不是这个意思。

它的意思是“选择一个类型,任何类型。我不知道你会选择什么,所以现在我们称之为'T。选择类型了吗?好!现在我'将采用该类型的参数"

换句话说,选择类型参数的是函数的调用者,而不是实现者。

与其他通用函数比较。我们以List.length 为例。它具有以下签名:

List.length<'a> : 'a list -> int

这个签名说:“选择任何类型'a。任何类型,我不在乎。然后我会获取'a list类型的列表并返回int"

它的实现者不能随意决定将某些特定列表作为参数 - 例如int list, - 不,他们必须以适用于任何类型的方式编写函数。因为这就是类型签名所承诺的。

这意味着,如果您按照定义的方式定义了IWrapper 接口,则方法必须能够接受任何 类型,而不仅仅是FooBar。因为这就是方法的类型签名所承诺的。


但在我看来,您真正想做的是创建一个特定于类型的包装器。一个包装器,可以包装和展开某些特定的两种类型,而不仅仅是一般的任何类型。是这样吗?

如果是这样,那么类型参数应该在接口上,而不是在方法上:

type IWrapper<'T, 'U> =
    abstract member Unwrap : 'T -> string
    abstract member Wrap : string -> 'U

这个定义是说“如果你有一个IWrapper&lt;a, b&gt;的实例,那么你可以用它来解开a或包装b,没有别的

现在您可以专门为FooBar 创建一个包装器:

let foobarWrapper =
    { new IWrapper<Foo, Bar> with
        member this.Unwrap foo = unwrapFoo foo
        member this.Wrap string = Bar string }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    • 1970-01-01
    • 2010-11-11
    • 2019-03-22
    相关资源
    最近更新 更多