I'm having trouble understanding the behavior of F#'s type inferencer. The operator string
relies on static type dispatch at compile-time, and not run-time, which is why something like let lowerstring = string >> (fun s -> s.ToLowerInvariant())
isn't generalized - the compiler needs to know the type of the argument. However, I'm seeing some strange-to-me behavior.
With the following definitions:
let inline lower (s: string) = s.ToLowerInvariant()
// 'T -> string, constrained to the type of its first use
let lowerstring1 = string >> lower
// Same as above
let lowerstring2 value = value |> string |> lower
// Same as above
let lowerstring3 = box >> string >> lower
// 'a -> string, fully generalized
let lowerstring4 value = value |> box |> string |> lower
I observe this behavior:
// val token: JToken
// val num: int
let tokstr1 = lowerstring1 token // lowerstring1 now has type JToken -> string
let numstr1 = lowerstring1 num // Error, doesn't compile
(* As above with lowerstring2 and lowerstring3 *)
let tokstr4 = lowerstring4 token // lowerstring4 now has type 'a -> string
let numstr4 = lowerstring4 num // no error, works as 'expected'
I'm not clear why lowerstring3
and lowerstring4
are typechecked differently. Static constraints seem likely, but if that were the case then shouldn't lowerstring4
have failed to generalize? Why should the explicit presence of a function argument make the difference here?
F# has to codegen down to IL, and since there are no generic 'values' in .NET, functions are only made when they are true syntatic functions (which get generated into a .NET method which can be generic).
(One exception is [<GeneralizableValueAttribute>]
, see eg here .)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.