[英]F# member constraints + ^a byref parameters
在玩过F#成员约束功能并编写如下函数之后:
let inline parse< ^a when ^a : (static member Parse: string -> ^a) > s =
(^a: (static member Parse: string -> ^a) s)
那很好用:
let xs = [ "123"; "456"; "999" ] |> List.map parse<int>
我正在尝试编写其他func tryParse
,它使用静态方法TryParse
并将解析结果包装为'a option
类型,以便在F#中提供更好的支持。 像这样的东西不能编译:
let inline tryParse s =
let mutable x = Unchecked.defaultof< ^a>
if (^a: (static member TryParse: string * ^a byref -> bool) (s, &x))
then Some x else None
错误是:
错误FS0001:该表达式应具有类型byref <'a>,但此处具有类型'a ref
F# ref
-cells也不起作用:
let inline tryParse s =
let x = ref Unchecked.defaultof< ^a>
if (^a: (static member TryParse: string * ^a byref -> bool) (s, x))
then Some x else None
我究竟做错了什么?
更新
这似乎已在F#3.0中修复。
旧答案:
我同意Stephen的评论,即最有可能是错误。 Byref类型有很多限制,因此它们在成员约束方面的表现不佳对我来说并不奇怪。 这是使用反射的一种(丑陋的)解决方法:
type parseDel<'a> = delegate of string * 'a byref -> bool
type Parser< ^a when ^a : (static member TryParse: string * ^a byref -> bool)> private ()=
static let parser = System.Delegate.CreateDelegate(typeof<parseDel<'a>>, typeof<'a>.GetMethod("TryParse", [|typeof<string>; typeof<'a>.MakeByRefType()|])) :?> parseDel<'a>
static member inline ParseDel = parser
let inline tryParse (s:string) =
let mutable x = Unchecked.defaultof< ^a>
if Parser<_>.ParseDel.Invoke(s, &x) then
Some x
else None
let one : int option = tryParse "1"
我认为这也是一个错误,它具有成员约束和byref类型。 我可以通过更改成员约束的签名来制作稍微丑陋的反射版本:
let inline tryParse<'a when 'a : (static member TryParse : string -> 'a byref -> bool)> s =
let args = [| s ; null |]
if typeof<'a>
.GetMethod("TryParse", [| typeof<string>; typeof< ^a>.MakeByRefType() |])
.Invoke(null, args) = box true
then Some (args.[1] :?> 'a)
else None
这个非常接近:
let inline tryParse< ^a when ^a: (static member TryParse: string -> ^a byref -> bool)> s =
let mutable x = Unchecked.defaultof<'a>
if (^a: (static member TryParse: string -> ^a byref -> bool) (s, &x))
then Some x else None
但出现错误FS0421:尝试编译它时,此时无法使用变量'x'的地址 。
这样可以编译,但是仍然无法正常工作:
let inline tryParse< ^a when ^a: (static member TryParse: string -> ^a ref -> bool) > s =
let x = ref Unchecked.defaultof< ^a>
match (^a: (static member TryParse: string -> ^a ref -> bool ) (s, x)) with
| false -> None
| true -> Some(!x)
// returns [Some 0; Some 0; Some 0; null], so our tryParse is not retrieving the value from the ref
let xs = [ "1"; "456"; "999"; "a" ] |> List.map tryParse<int>
在这种特定情况下,我不会使用f#中的Parse重新创建TryParse,而不是使用反射。
let inline tryParse< ^a when ^a: (static member Parse: string -> ^a) > s =
try
Some(^a: (static member Parse: string -> ^a) s)
with
| exn -> None
let xs = [ "1"; "456"; "999"; "a" ] |> List.map tryParse<int>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.