F# 签名文件和对应的实现文件中的通用函数定义
Posted
技术标签:
【中文标题】F# 签名文件和对应的实现文件中的通用函数定义【英文标题】:Generic function definitions in F# signature files and corresponding implementation file 【发布时间】:2013-04-18 05:22:25 【问题描述】:我正在尝试使用 F# 签名文件为轻量级数据存储模块创建抽象。这是我的签名文件代码,假设它被称为repository.fsi
namespace DataStorage
/// <summary>Lightweight Repository Abstraction</summary>
module Repository =
/// <summary> Insert data into the repository </summary>
val put: 'a -> unit
/// <summary> Fetch data from the repository </summary>
val fetch: 'a -> 'b
/// <summary> Remove data from the repository </summary>
val remove: 'a -> unit
这里是对应的实现,我们称之为repository.fs
namespace DataStorage
module Repository =
(* Put a document into a database collection *)
let put entity = ()
(* Select a document from a database collection *)
let fetch key = ("key",5)
(* Remove a document from a database collection *)
let remove entity = ()
在我的 Visual Studio 项目文件中,我有上面的签名文件 (repository.fsi) 我的实现文件(repository.fs)。 put 和 remove 函数正在被正确解析和验证,没有错误(在实现文件中),但 fetch 函数一直给我红色在 Visual Studio 中波浪形地显示以下错误消息:
模块“DataStorage.Repository”包含
val fetch: s:string -> string * int
但它的签名指定
val fetch<'a,'b> : 'a -> 'b
各自的类型参数计数不同
谁能告诉我我做错了什么?我的 fetch 函数值是否定义错误 我的签名文件?我只是想在我的签名文件中创建一个通用函数('a -> 'b),并让实现将一种类型作为输入并返回另一种类型作为输出。
【问题讨论】:
fetch
的签名是通用的,但实现不是。如果实现只是一个存根,请尝试将其替换为 let fetch key = Unchecked.defaultof<_>
以使其能够编译。
感谢@Daniel 的回答,我试试看。
【参考方案1】:
我在 F# 方面还不是很强,但我认为您在这里以错误的方式使用签名文件。
首先。以下是修复编译错误的方法:
替换:
let fetch key = ("key",5)
与:
let fetch key = ("key",5) :> obj :?> 'b
而且您不会收到编译错误。 但这个修复在很多情况下实际上没有意义。例如,如果下一个工作:
let a = Repository.fetch(56)
如果您明确指定类型,它将崩溃(在大多数情况下):
let b = Repository.fetch<int, string>(56)
情况是泛型实现应该使用泛型类型。如果我正确理解了您要做什么,那么当使用签名文件隐藏实现方面时,您应该使用 OOP 和多态性。例如:
namespace DataStorage
[<AbstractClass>]
type Repository<'TKey,'T>() =
abstract member put: 'T -> unit
abstract member fetch: 'TKey -> 'T
abstract member remove: 'T -> unit
type IntRepository() =
inherit Repository<int, int>()
override self.put item = ()
override self.fetch index = 5
override self.remove item = ()
type StringRepository() =
inherit Repository<int, string>()
override self.put item = ()
override self.fetch index = "Hello"
override self.remove item = ()
【讨论】:
这是有道理的。我正在尝试以与在 OCaml 中相同的方式使用签名文件。我想这是在 .NET 世界中做事的标准方式。感谢@petro.silovskyy 的时间、想法和解释。【参考方案2】:我最近尝试过的一个(一些限制性的)替代方案与泛型相差无几,但似乎适用于我的场景。基本上 fetch 函数的签名文件现在看起来像这样。
'a -> RepositoryRecord
RepositoryRecord 的实现是代数数据类型。
type RepositoryRecord = | SomeVal1 of int * string | SomeVal2 of string
【讨论】:
我认为这根本没有限制。我一直使用这种方法。事实上,我什至会说这是惯用的。 @Shredderroy 感谢您的评论。很高兴知道我的想法并非完全错误。 ;) 确定。也许更有知识的人会权衡,但关于你的评论...one step away from generics
,我只想说我不相信你最初的方法涉及generics
。毕竟,generics
和 casting
之间是有区别的,我相信这是你最终不得不做的事情。以上是关于F# 签名文件和对应的实现文件中的通用函数定义的主要内容,如果未能解决你的问题,请参考以下文章