【发布时间】: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