给定以下代码:
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#中不能是多态的。它们可以使用泛型类型参数('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 -> unit) -> unit
可以被视为forall 'a . ('a -> unit) -> unit
。
当你调用函数时,你需要指定'a
是什么,并且不能改变(即,一旦'a
固定,你就不能使用函数'a -> unit
作为'a
的两种不同类型的参数)。
使用通用类型,您可以自己编写forall
,因此您可以说类型为:(forall 'a . 'a -> unit) -> unit
。现在,通用形参'a
只链接到作为实参的函数。作为参数给出的函数的类型现在本身是一个泛型函数,因此您可以使用代表'a
的不同类型调用它。
PS:值限制是一个不同的问题-这本质上意味着f#不能使非语法函数的东西泛型,但在您的示例中,您正在编写语法函数,所以这不是这里的问题。
也可以
let NotWorking (display:(obj -> unit) -> obj list -> unit) =
display (printfn "%O") [1;2;3]
display (printfn "%O") ["a";"b";"c"]