Вы можете воспроизвести это предупреждение в очень простом тестовом примере, убрав часть шума из вашего примера:
let f (x: 'a) =
string x
Глядя на это, вы можете быть смущены, потому что тип string
функция 'T -> string
, но это не так просто. Чтобы понять, что происходит, вы должны взглянуть на реализацию функции string
в FSharp.Core:
let inline anyToString nullStr x =
match box x with
| null -> nullStr
| :? System.IFormattable as f -> f.ToString(null,System.Globalization.CultureInfo.InvariantCulture)
| obj -> obj.ToString()
[<CompiledName("ToString")>]
let inline string (value: ^T) =
anyToString "" value
// since we have static optimization conditionals for ints below, we need to special-case Enums.
// This way we'll print their symbolic value, as opposed to their integral one (Eg., "A", rather than "1")
when ^T struct = anyToString "" value
when ^T : float = (# "" value : float #).ToString("g",CultureInfo.InvariantCulture)
when ^T : float32 = (# "" value : float32 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : int64 = (# "" value : int64 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : int32 = (# "" value : int32 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : int16 = (# "" value : int16 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : nativeint = (# "" value : nativeint #).ToString()
when ^T : sbyte = (# "" value : sbyte #).ToString("g",CultureInfo.InvariantCulture)
when ^T : uint64 = (# "" value : uint64 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : uint32 = (# "" value : uint32 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : int16 = (# "" value : int16 #).ToString("g",CultureInfo.InvariantCulture)
when ^T : unativeint = (# "" value : unativeint #).ToString()
when ^T : byte = (# "" value : byte #).ToString("g",CultureInfo.InvariantCulture)
Это использует статически разрешенные параметры типа и использует явную реализацию для каждого из перечисленных типы, так что на самом деле это не так обобщенно c, как кажется из сигнатуры типа. В вашем случае происходит вывод, что он выводит наиболее совместимый тип, а поскольку ваша функция просто набрана как 'a
, она выбирает obj
. Итак, потому что вы вызываете string
, и ваш входной параметр принудительно относится к одному из типов, которые фактически обрабатывает функция string
(что на самом деле в anyToString
), и это obj
.
Чтобы заставить его работать в вашем реальном сценарии, на самом деле довольно просто: просто сделайте ваши функции встроенными и вообще не указывайте тип параметра:
let inline exists key =
r.Exists(string key)
Это будет выведите тип для параметра и вызовите правильную версию string
, и это будет работать практически со всем, что вы хотите передать, включая ваши перечисления.