I ran into some unexpected behavior from the F# compiler recently. I was able to find a workaround, but the original behavior baffles me and I wanted to see if anyone can help me understand what causes it.
A function that I had defined as non-generic was becoming generic, which interfered with the ability of the function to share state between multiple invocations. I simplified my use-case down to the following:
let nextId =
let mutable i = 0
let help (key:obj) =
i <- i + 1
i
help
nextId "a" // returns 1
nextId "b" // also returns 1!!!!
Why is nextId of type 'a -> int instead of obj -> int? Clearly the generalization is also responsible for the bug where it returns 1 repeatedly, but why is the generalization occurring in the first place?
Note that if I define it without naming the nested function, it works as expected in giving unique Ids:
let nextId =
let mutable i = 0
fun (key:obj) ->
i <- i + 1
i
nextId "a" // returns 1
nextId "b" // returns 2
But even more mysterious, with this definition, F# Interactive can't decide whether nextId is an (obj -> int) or an ('a -> int). When I first define it I get
val nextId : (obj -> int)
but if I simply eval
nextId
I get
val it : ('a -> int)
What's going on here and why does my simple function get automatically generalized?