【问题标题】:F# member constraints + ^a byref parametersF# 成员约束 + ^a byref 参数
【发布时间】:2011-06-07 02:40:14
【问题描述】:

在玩过 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 但这里有类型 '一个参考

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

我做错了什么?

【问题讨论】:

  • 哎呀,我认为这是一个错误......另外,TryParse: string -&gt; bool * ^a 不起作用。
  • 这似乎已在 F# 3.0 中修复。

标签: generics f# constraints ref byref


【解决方案1】:

更新

这似乎已在 F# 3.0 中修复。

旧答案:

我同意 Stephen 的评论,即这很可能是一个错误。 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"

【讨论】:

    【解决方案2】:

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

    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>
    

    【讨论】:

    • 我将此提交给 fsbugs@microsoft.com
    【解决方案3】:

    我认为这也是一个错误,与成员约束和 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”的地址

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-20
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多