Pat*_*ald 3 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"]
~~~ ~~~ ~~~
Run Code Online (Sandbox Code Playgroud)
最后一行给出了错误: This expression was expected to have type int but here has type string
我认为以下可能有效,但它没有:
let StillNotWorking (display: ('a -> unit) -> seq<'a> -> unit) =
Run Code Online (Sandbox Code Playgroud)
我的问题是,如何定义NotWorking函数以使显示参数在函数内保持通用?
作为参数传递给其他函数(例如你的函数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"]
Run Code Online (Sandbox Code Playgroud)
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#不能使那些不是语法函数的东西变得通用,但在你的例子中,你正在编写语法函数,所以这不是问题.
这也有效
let NotWorking (display:(obj -> unit) -> obj list -> unit) =
display (printfn "%O") [1;2;3]
display (printfn "%O") ["a";"b";"c"]
Run Code Online (Sandbox Code Playgroud)