问题
I want to define a function that accepts an optional argument which is a function ('a -> 'b). The default value should be the identity, which is actually ('a -> 'a), but i see no reason why it should not be compatible with the more general ('a -> 'b). When i try:
let optional_apply ?f i =
match f with
| None -> i + 4
| Some fn -> fn (i + 4)
I always get the narrow type ?f:(int -> int) -> int -> int. But I want to keep f as int -> 'b. What can i do? Or is this just unsound, since optional_apply would not have a definite type? If so, how would I get a similar functionality?
回答1:
It is impossible, the f argument must not be optional. A simple explanation, is that optional parameters are just a syntactic sugar.
So without sugar your function can be rewritten in a following form:
let optional_apply f n = match f with
| Some f -> f (n + 4)
| None -> (n + 4)
And here typechecker allows us only one type for f: int -> int. If it allowed us an int -> 'a type, then the None path of expression would be unsound. In other words, depending on whether f is None or Some the optional_apply will evaluate to different types. And this is considered unsound.
The best way to prove the unsoundness is to give a simple example where typechecker will allow unsound program:
let f = ref None
let g n = optional_apply ?f:!f n
with this definitions, if type checker allowed f parameter to remain polymorphic, then we can at any time "break" g function by changing f reference to anything else.
回答2:
This is not compatible with the general type ('a -> 'b). Here's why:
The first pattern,
None -> i + 4is of typeint, thus restricting the function's return type toint.The second pattern,
Some fn -> fn (i + 4)must then also be of typeint. Because(i + 4)is of typeint, too,fnmust take and return anint, thusint -> int.
The best alternative I can think of is Haskell's maybe function, of type ('a -> 'b) -> 'b -> 'a option -> 'b:
let maybe fn backup opt = match opt with
| Some v -> fn v
| None -> backup
;;
... which you can probably adapt to your use case.
来源:https://stackoverflow.com/questions/28267730/how-to-force-ocaml-to-infer-a-more-general-type