如何在 F# 中声明泛型参数?

How do I declare a generic parameter in F#?

提问人:Patrick McDonald 提问时间:12/8/2011 最后编辑:Patrick McDonald 更新时间:12/9/2011 访问量:450

问:

给定以下代码:

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 函数,以便显示参数在函数中保持通用?

F# 值限制

评论


答:

6赞 Tomas Petricek 12/8/2011 #1

作为参数传递给其他函数(如 your )的函数本身在 F# 中不能是多态的。它们可以使用泛型类型参数(等),但此参数的实际类型是在调用 main 函数(在本例中为)时指定的。这意味着您只能使用用于 主体中类型变量的单个实际类型进行调用。display'aNotWorkingdisplay'aNotWorking

作为解决方法,您可以将接口与泛型方法一起使用:

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"] 

接口的方法本身就是泛型方法,因此可以使用不同的类型参数多次调用该方法(在第一种情况下,在第二种情况下)。Displayintstring

但是,在经常用 F# 编写普通代码时,我并没有发现这是一个限制,所以也许有一个更简单的解决方案来解决您的问题(可能,采用非泛型或类似的东西 - 或者像 John 的答案一样)。如果您提供有关实际代码的更多详细信息,那将很有用。IEnumerableobj list

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

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

当你调用这个函数时,你需要指定什么是不能改变的,什么是不能改变的(即你不能将你得到的函数作为参数使用,因为两种不同类型的参数是固定的)。'a'a -> unit'a'a

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

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

5赞 John Palmer 12/8/2011 #2

这也有效

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

评论

0赞 Tomas Petricek 12/8/2011
+1 这绝对是在实践中解决它的最简单方法。我无法想象您实际上需要泛型函数作为参数的场景。display
0赞 Patrick McDonald 12/9/2011
感谢您的回答,它确实解决了上述问题,但是我过度简化了我的问题,并在我的真实代码中遇到了协方差问题(我认为)。这种方法肯定要简单得多,一般来说应该可以解决很大一部分类似的问题。