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

时间:2011-12-08 01:06:59

标签: f# value-restriction

给出以下代码:

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

2 个答案:

答案 0 :(得分:6)

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

作为一种变通方法,您可以使用具有通用方法的接口:

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 -> unit) -> unit可以被视为forall 'a . ('a -> unit) -> unit

当您调用该函数时,您需要指定'a是什么并且无法更改(即您不能使用您获得的函数'a -> unit作为具有两种不同类型的参数'a修复后'a

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

PS:价值限制是一个不同的问题 - 这实际上意味着F#不能使那些不是语法函数的东西变得通用,但在你的例子中,你正在编写语法函数,所以这不是问题所在。

答案 1 :(得分:5)

这也适用

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