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

問題は、doSomething2タイプがの場合((string->'a) -> 'b) -> unit'aおよび'bがの各呼び出し中に修正されるdoSomething2ことです。これは、必要なものではありません。この場合、の'a両方として、intおよび1回の呼び出しfloat中に処理する必要があります。doSomething2

あなたが本当に望んでいるのは次のようなもののようですdoSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unitが、そのような直接全称記号はF#には存在しません。ご存知のように、これを回避する方法は、ジェネリックメソッドで型を使用することです。

また、コメントで述べたように、F#がタイプをサポートしていてもforall、推論は不可能です。doSomething2あなたの関数を考えてみてください-ある出力型への型の入力と、ある(おそらく異なる)出力型へrunnerの型の入力を受け取ることができる必要があることを私たちは知っています。すべてがこの要件を満たすためのいくつかの異なる署名を次に示します。string -> intstring -> floatdoSomething2

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

これらのタイプはどれも他のタイプより一般的ではなく、すべて互換性がないことに注意してください。最初のケースではid、関数に渡すことができ、2番目のケースでは、関数に渡すことができrun4、3番目のケースでは、関数に渡すことができignoreます(ただし、これらの関数は、他の可能なシグネチャと互換性がありません!)。

于 2012-07-18T18:35:33.683 に答える