4

给定以下代码:

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 参数在函数中保持通用?

4

2 回答 2

6

作为参数传递给其他函数(如您的display)的函数本身在 F# 中不能是多态的。他们可以使用泛型类型参数('a等),但此参数的实际类型是在调用主函数(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如 John 的回答)。如果您提供有关实际代码的更多详细信息,那将很有用。

一些背景知识,以防您对理论细节感兴趣,但这些都不是日常现实世界 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# 不能使不是语法函数的东西通用,但在你的例子中,你正在编写语法函数,所以这不是这里的问题。

于 2011-12-08T01:12:41.710 回答
5

这也有效

let NotWorking (display:(obj -> unit) -> obj list -> unit) =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
于 2011-12-08T01:20:11.740 回答