简体   繁体   中英

Why isn't my function generalized intuitively?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM