我很确定我遇到了某种限制,但我不明白:
type IRunner =
abstract member Run : (string -> 'a) -> 'a
type T() =
let run4 doFun = doFun "4"
let run5 doFun = doFun "5"
let parseInt s = System.Int32.Parse(s)
let parseFloat s = System.Double.Parse(s)
let doSomething () =
let i = parseInt |> run4
let f = parseFloat |> run4
f |> ignore
// Make it more generic ->
//let doSomething2 (runner:(string->'a)->'b) =
let doSomething2 runner =
// Error on the following lines with both declarations
let i = parseInt |> runner
let f = parseFloat |> runner
f |> ignore
// Want to do something like
let test () =
doSomething2 run4
doSomething2 run5
// Workaround
let workaround (runner:IRunner) =
let run f = runner.Run f
let i = parseInt |> run
let f = parseFloat |> run
f |> ignore
有人可以为此带来一些启示吗?我没有找到任何相关的问题,对不起,如果我复制了什么。
答案 0 :(得分:5)
问题是,如果doSomething2
的类型为((string->'a) -> 'b) -> unit
,则在'a
的每次调用期间都会修复'b
和doSomething2
,这不是什么您希望 - 在{em}单{/ em>调用'a
期间,int
需要同时视为float
和doSomething2
。
看起来你真正想要的更像是:doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit
,但F#中不存在那种直接的通用量化。正如您所发现的,解决此问题的方法是使用具有泛型方法的类型。
即使F#支持forall
类型,正如我在评论推断中提到的那样仍然是不可能的。考虑一下您的doSomething2
函数 - 我们知道runner
需要能够将类型string -> int
的输入转换为某些输出类型,并将类型string -> float
的输入转换为某些输出类型(可能)不同的)输出类型。以下是doSomething2
的几个符合此要求的不同签名:
forall 'a. 'a -> 'a
forall 'a. (string -> 'a) -> 'a
forall 'a. 'a -> unit
请注意,这些类型中没有一种比其他类型更通用,它们都不兼容。在第一种情况下,我们可以将id
传递给函数,在第二种情况下,我们可以将run4
传递给它,在第三种情况下,我们可以将ignore
传递给它(但这些功能都不与其他可能的签名兼容!)。