首页 文章

F#成员约束^ a byref参数

提问于
浏览
8

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

我究竟做错了什么?

3 回答

  • 1

    UPDATE

    这似乎在F#3.0中得到修复 .

    Old answer:

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

    我认为这也是一个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'的地址 .

  • 5

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

    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>
    

相关问题