2

我很确定我遇到了某种限制,但我不明白:

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

有人可以对此有所了解吗?我没有找到任何相关的问题,对不起,如果我重复了一些东西。

4

1 回答 1

5

问题是,如果有doSomething2type ,((string->'a) -> 'b) -> unit然后在每次调用.'a'bdoSomething2'aintfloatdoSomething2

看起来你真正想要的更像是:doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit,但这种直接的全称量化在 F# 中不存在。正如您所发现的,解决此问题的方法是使用具有泛型方法的类型。

即使 F# 确实支持forall类型,正如我在评论推断中提到的那样,仍然不可能。考虑您的doSomething2功能-我们知道runner需要能够将输入类型string -> int转换为某些输出类型,并将类型输入string -> float转换为某些(可能不同的)输出类型。这里有几个不同的签名doSomething2都满足这个要求:

  1. forall 'a. 'a -> 'a
  2. forall 'a. (string -> 'a) -> 'a
  3. forall 'a. 'a -> unit

请注意,这些类型中没有一个比其他类型更通用,它们都是不兼容的。在第一种情况下,我们可以传递id给函数,在第二种情况下,我们可以传递run4给它,而在第三种情况下,我们可以传递ignore给它(但这些函数都不与其他可能的签名兼容!)。

于 2012-07-18T18:35:33.683 回答