【问题标题】:How do I declare a generic parameter in F#?如何在 F# 中声明泛型参数?
【发布时间】:2012-01-15 12:44:50
【问题描述】:

给定以下代码:

let DisplayImpl logger data =
    data |> Seq.iter logger
    printfn ""

let Working =
    DisplayImpl (printfn "%O") [1;2;3]
    DisplayImpl (printfn "%O") ["a";"b";"c"]

let NotWorking display =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
                            ~~~ ~~~ ~~~

最后一行给出错误:This expression was expected to have type int but here has type string

我认为以下方法可能有效,但它没有:

let StillNotWorking (display: ('a -> unit) -> seq<'a> -> unit) =

我的问题是,如何定义 NotWorking 函数以使 display 参数在函数中保持通用?

【问题讨论】:

    标签: f# value-restriction


    【解决方案1】:

    作为参数传递给其他函数的函数(例如您的display)在 F# 中不能是多态的。他们可以使用泛型类型参数('a 等),但是在调用主函数(在您的情况下为 NotWorking)时指定此参数的实际类型。这意味着您只能在 NotWorking 的主体中使用用于类型变量 'a 的单个实际类型来调用 display

    作为一种解决方法,您可以使用带有通用方法的接口:

    type Displayer = 
      abstract Display : (obj -> unit) -> 'T list -> unit
     
    let NotWorking (display:Displayer) = 
        display.Display (printfn "%O") [1;2;3] 
        display.Display (printfn "%O") ["a";"b";"c"] 
    

    接口的方法Display本身就是泛型方法,所以你可以用不同的类型参数多次调用该方法(int在第一种情况下,string在第二种情况下)。

    但是,当我经常在 F# 中编写普通代码时,我并没有发现这是一个限制,所以也许有一个更简单的解决方案可以解决您的问题(可能采用非通用 IEnumerable 或类似的简单方法 - 或obj list 来自约翰的回答)。如果您提供有关实际代码的更多详细信息,那将很有用。

    一些背景知识,以防万一您对理论细节感兴趣,但这些都不是在日常现实世界的 F# 编程中真正重要的东西。无论如何-

    这在 Haskell 等其他语言中是可能的,允许它的机制称为通用类型。当你在 F# 中有一个多态函数时,本质上意味着类型变量的作用域是整个函数,所以('a -&gt; unit) -&gt; unit 可以看成forall 'a . ('a -&gt; unit) -&gt; unit

    当您调用该函数时,您需要指定'a 是什么并且不能更改(即,您不能使用函数'a -&gt; unit 作为'a 的两种不同类型的参数一次'a 已修复)。

    使用通用类型,可以自己写forall,所以可以说类型是:
    (forall 'a . 'a -&gt; unit) -&gt; unit。现在泛型参数'a 仅链接到您将作为参数获得的函数。作为参数给出的函数类型现在本身就是一个泛型函数,因此您可以使用代表'a 的不同类型来调用它。

    PS:值限制是一个不同的问题 - 这本质上意味着 F# 不能使不是语法函数的东西通用,但在您的示例中,您正在编写语法函数,所以这不是这里的问题。

    【讨论】:

      【解决方案2】:

      这个也可以

      let NotWorking (display:(obj -> unit) -> obj list -> unit) =
          display (printfn "%O") [1;2;3]
          display (printfn "%O") ["a";"b";"c"]
      

      【讨论】:

      • +1 这绝对是实践中最简单的解决方法。我无法想象你实际上需要一个通用的 display 函数作为参数的场景。
      • 感谢您的回答,它确实解决了上述问题,但是我过度简化了我的问题并在我的真实代码中遇到了协方差问题(我认为)。这种方法肯定要简单得多,一般应该可以解决大部分类似问题。
      猜你喜欢
      • 2018-10-03
      • 1970-01-01
      • 1970-01-01
      • 2012-10-17
      • 1970-01-01
      • 2016-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多