After some playing around F# member constraints feature and writing function like this:
let inline parse< ^a when ^a : (static member Parse: string -> ^a) > s =
(^a: (static member Parse: string -> ^a) s)
That works perfectly fine:
let xs = [ "123"; "456"; "999" ] |> List.map parse<int>
I'm trying to write other func tryParse
, that uses static method TryParse
and wraps the parse result into 'a option
type for better support in F#. Something like this doesn't compiles:
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
The error is:
error FS0001: This expression was expected to have type byref<'a> but here has type 'a ref
F# ref
-cells doesn't work too:
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
What am I doing wrong?
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"
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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With