Tom*_*han 4 generics f# type-constraints
我正在尝试创建一个泛型函数,它需要它的类型参数,它是一个记录类型,并且它有一个特定的属性.这是一个生成相关编译器错误的示例:
let foo<'a> (a : 'a) =
a' = { a with bar = "baz" }
a'
Run Code Online (Sandbox Code Playgroud)
编译这个我得到一个错误说明The record label bar is not defined.
我尝试添加以下类型约束:
let foo<'a when 'a : (member Id : string)> =
// ...
Run Code Online (Sandbox Code Playgroud)
但那也没有编译,抱怨说 This code is not sufficiently generic. The type variable ^a when ^a : (member get_Int : ^a -> string) could not be generalized because it would escape its scope.
有没有办法指定一个允许我正确执行此操作的类型约束?
我不认为有一种方法可以使用静态成员约束来指定这一点 - 静态成员约束受到相当大的限制,它们主要是一种抽象机制,除了其他更常用的技术之外还可用。
如果我试图解决这样的问题,我可能会考虑使用接口(这并不总是最好的方法,但在不了解您的具体情况的情况下,这可能是一种合理的默认方法):
type ISetA<'T> =
abstract WithA : string -> 'T
type MyRecord =
{ A : string }
interface ISetA<MyRecord> with
member x.WithA(a) = { x with A = a }
Run Code Online (Sandbox Code Playgroud)
在实现记录时,您需要添加一个接口实现(因此,与仅使用静态成员约束相比,您需要做更多的工作)。但是,您还明确表示这是该类型的预期用途......
用法也比使用静态约束更简单:
let setA (setA:ISetA<_>) =
setA.WithA "Hello"
setA { A = "Test" }
Run Code Online (Sandbox Code Playgroud)
我建议先阅读托马斯的回答.在可能的情况下,通常应避免使用静态解析的类型约束.它们是F#编译器的一个特性而不是.NET,因此它们在某种程度上限制了代码的可重用性.也就是说,它们非常强大,并允许您在编译时强加有用的约束.
使用它们的语法也不是非常令人愉快,但如果你仍然没有被阻止,你可以做这样的事情:
type Test = {Bar : string}
let inline foo (a : ^a) =
"foo " + ((^a) : (member Bar : string) (a))
let foobar = foo {Bar = "bar"} // prints "foo bar"
Run Code Online (Sandbox Code Playgroud)
但请注意,您实际上不能将类型限制为记录,只是具有Bar类型成员的东西string.所以这也将解决:
type Test2(str : string) = member this.Bar = str
let foobar2 = foo (Test2("bar")) // prints "foo bar"
Run Code Online (Sandbox Code Playgroud)