Reputation: 1704
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.
Upvotes: 2
Views: 388
Reputation: 55184
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
.
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.
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
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!).
Upvotes: 5