F#成员约束+ ^ a byref参数

时间:2011-01-11 11:00:37

标签: generics f# constraints ref byref

在玩了一些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>

我正在尝试编写其他函数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&lt;'a&gt; 但这里有类型       '参考

F#ref - 细胞也不起作用:

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

我做错了什么?

3 个答案:

答案 0 :(得分:5)

<强>更新

这似乎是在F#3.0中修复的。

旧回答:

我同意斯蒂芬的评论,认为这很可能是一个错误。 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"

答案 1 :(得分:1)

我认为这也是一个bug,有成员约束和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'的地址

答案 2 :(得分:1)

这会编译,但仍无法按预期工作:

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>