可能的F#类型推断限制 [英] Possible F# type inference limitation
问题描述
我很确定自己会遇到某种限制,但我不明白:
I'm quite sure that I run into some kind of limitation, but I do not understand it:
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
有人可以对此有所启发吗?我没有找到任何相关的问题,对不起,如果我重复了一些事情.
Can somebody bring some light over this? I did not find any related question, sorry if i duplicated something.
推荐答案
问题是,如果doSomething2
具有类型((string->'a) -> 'b) -> unit
,则'a
和'b
在每次调用doSomething2
时都是固定的,这不是您想要的-在您的情况下,在'a
需要同时作为int
和float
对待.
The problem is, if doSomething2
has type ((string->'a) -> 'b) -> unit
, then 'a
and 'b
are fixed during each invocation of doSomething2
, which isn't what you want - in your case 'a
needs to treated as both int
and float
during a single invocation of doSomething2
.
您似乎真正想要的更像是:doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit
,但是F#中不存在这种直接的通用量化.如您所见,解决此问题的方法是将类型与泛型方法一起使用.
It seems like what you really want is more like: doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit
, but that kind of direct universal quantification doesn't exist in F#. As you've discovered, the way to work around this is to use a type with a generic method.
即使F#支持forall
类型,正如我在评论中提到的那样,仍然是不可能的.考虑您的doSomething2
函数-我们知道runner
需要能够将类型为string -> int
的输入转换为某种输出类型,并将类型为string -> float
的输入转换为某种(可能是不同的)输出类型.以下是doSomething2
的几个不同的签名,都满足此要求:
And even if F# did support forall
types, as I mentioned in a comment inference still wouldn't be possible. Consider your doSomething2
function - we know that runner
needs to be able to take an input of type string -> int
to some output type and an input of type string -> float
to some (possibly different) output type. Here are several different signatures for doSomething2
that all meet this requirement:
-
forall 'a. 'a -> 'a
-
forall 'a. (string -> 'a) -> 'a
-
forall 'a. 'a -> unit
forall 'a. 'a -> 'a
forall 'a. (string -> 'a) -> 'a
forall 'a. 'a -> unit
请注意,这些类型都不比其他类型更通用,它们都是不兼容的.在第一种情况下,可以将id
传递给函数,在第二种情况下,可以将run4
传递给该函数,在第三种情况下,可以将ignore
传递给该函数(但是这些函数都不是与其他可能的签名兼容!).
Note that none of these types is more general than the others, they are all incompatible. In the first case, we could pass id
to the function, in the second case, we could pass run4
to it, and in the third case, we could pass ignore
to it (but none of those functions is compatible with the other possible signatures!).
这篇关于可能的F#类型推断限制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!