F# member constraints + ^a byref parameters

前端 未结 3 760
执念已碎
执念已碎 2020-12-17 21:03

After some playing around F# member constraints feature and writing function like this:

let inline parse< ^a when ^a : (static member Parse: string ->          


        
相关标签:
3条回答
  • 2020-12-17 21:47

    This compiles but still does not work as expected:

    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>
    

    in this specific case, rather than using reflection I would just recreate TryParse out of Parse in f#

    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>
    
    0 讨论(0)
  • 2020-12-17 21:51

    UPDATE

    This appears to be fixed in F# 3.0.

    Old answer:

    I agree with Stephen's comment that it's most likely a bug. There are many limitations on byref types, so it's not particularly surprising to me that they don't play well with member constraints. Here's an (ugly) workaround using reflection:

    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"
    
    0 讨论(0)
  • 2020-12-17 21:53

    I think it is a bug too, something with member constraints and byref types. I can make a slightly less ugly reflection version by changing the signature of the member constraint:

    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
    

    This one is very close:

    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
    

    but I get a error FS0421: The address of the variable 'x' cannot be used at this point when I try to compile it.

    0 讨论(0)
提交回复
热议问题