【问题标题】:Is it possible to write a genericFunction<’T> where ‘T is constrained to be a tuple of type 'T2*'T3?是否可以编写一个genericFunction<'T>,其中'T 被限制为'T2*'T3 类型的元组?
【发布时间】:2020-12-04 18:44:14
【问题描述】:

是否可以在 F# 中编写一个泛型函数,在其中指定类型 'T,但将 'T 约束为类型为 'T2*'T3 的元组?

以下是我尝试过的一些只会产生编译器错误的方法:

let fun1<'T when 'T :> 'T2*'T3> () = …
let fun2<'T, 'T2, 'T3 when 'T :> 'T2*'T3> () = …

(在 fun2 中,'T2/'T3 最好由 'T1 推断。)

我想这样做的原因是我正在编写一个带有内存数据库的应用程序,并且我正在尝试编写一个通用函数来提取与指定元组形式中的数据库实体相关联的数据.我有一个可行的解决方案,但有一些缺点:

let workingExtractEntity<'T> (id : EntityID) (db: Database) : EntityID*'T =
        let t = db.GetEntityComponent id typeof<'T>
        (id, t)

以及具有更多类型参数的类似函数:

let workingExtractEntity2<'T1, 'T2> (id : EntityID) (db: Database) : EntityID*'T1*'T2 = …

这种方法的主要问题是每次使用函数时都必须手动输入和排序通用参数。我希望能够指定单个元组类型,并结合常用元组类型的类型别名,如下所示:

//Obviously won’t compile, but illustrates what I would like to do.
let extractEntity2<'T when 'T :> EntityID*'T2*'T3> id db : 'T =
        let t2 = db.GetEntityComponent id typeof<'T2>
        let t3 = db.GetEntityComponent id typeof<'T3>
        (id, t2, t3)

type Player = EntityID*Position*Health
    
let extractPlayer id db : Player = extractEntity2<Player> id db

请注意,没有 Player 类型的对象存储在数据库中,而只有构成 Player 的组件(即 Position 和 Health)存储在那里。 (任何具有与其关联的位置和生命值的实体都可以表示为玩家。)

有没有办法实现这样的目标?

【问题讨论】:

  • 为什么需要使用泛型?难道你不能让类型推断确定函数的类型吗?或者,如果需要或需要,使用类型注释。当然,您可能需要为 EntityID、Position 和 Health 定义类型(看起来您已经这样做了,但这里没有透露类型定义)。
  • @ScottHutchinson 我需要泛型能够为不同类型重用相同的函数:可以从 extractEntity2 中删除泛型并将其重命名为“extractPlayer”,替换所有对 'T2 和 'T3 的引用与位置和健康。但是这个新的 extractPlayer 函数只能提取一个 Player,并且您必须为 extractMonster 和 extractFloorTile 等创建几乎相同的函数……关于类型推断:我的示例代码中函数的返回类型可能是推断,为了清楚起见,我只是将它们包括在内。
  • 考虑让 Player/Monster/FloorTile 成为一个泛型类而不是一个元组。见docs.microsoft.com/en-us/dotnet/fsharp/language-reference/…
  • 我认为这个问题可能与您正在尝试做的事情有关。 github.com/fsharp/fslang-suggestions/issues/629

标签: generics f#


【解决方案1】:

我做了一些研究,我想我已经找到了自己问题的答案:

不幸的是,我想做的事情在 F# 中似乎是不可能的,用户 Gus 很好地总结了原因here

恐怕没有办法给泛型添加子类型约束 类型参数基于 F# 中的另一个参数。他们总是被假定为 相等,请参阅规范 表单类型的新约束 :> 'b 是 再次解决为 type = 'b.

这个限制在 C# 中不存在,这使得互操作有时很麻烦。在 F# 语言建议 github 页面上有一个 issue 来解决它。

换句话说,你不能写这样的函数:

let foo<'T, 'U when 'T :> 'U> () = …

这意味着你也不能这样做:

let foo2<'T, 'T2, 'T3 when 'T :> Tuple<'T2, 'T3>> () = …

这基本上是我想要的,除了额外的类型推断,而不是输入整个:

let callFoo2 = foo2<int*string, int, string> ()

你可以这样写,然后从 'T1 推断出 'T2 和 'T3:

let callFoo2Differently = foo2<int*string> ()

【讨论】:

    猜你喜欢
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    相关资源
    最近更新 更多