Ограничения члена F # + ^ a byref параметры - PullRequest
8 голосов
/ 11 января 2011

После некоторой игры вокруг функции ограничений членов 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> но здесь есть тип 'ref

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 ]

5 голосов
/ 11 января 2011

UPDATE

Это исправлено в 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 голос
/ 05 февраля 2011

Это компилируется, но все равно не работает должным образом:

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>

в данном конкретном случае, вместо использования отражения, я просто воссоздаю TryParse из Parse в 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>
1 голос
/ 12 января 2011

Я думаю, что это тоже ошибка, что-то с ограничениями членов и типами 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' нельзя использовать в этот момент , когда я пытаюсь его скомпилировать.

...