Ограничение значения при отсутствии общих параметров
Я получаю ошибку ограничения значения на let makeElem
в следующем коде:
let elemCreator (doc: XmlDocument) =
fun name (value: obj) ->
let elem = doc.CreateElement(name)
match value with
| :? seq<#XmlNode> as childs ->
childs |> Seq.iter (fun c -> elem.AppendChild(c) |> ignore)
elem
| _ -> elem.Value <- value.ToString(); elem
let doc = new XmlDocument()
let makeElem = elemCreator doc
Почему я получаю ошибку ограничения значения, если анонимная функция возвращается из elemCreator
не имеет общих параметров?
Компилятор сообщает, что тип makeElem (string -> 'a -> XmlNode)
, Но почему это выводит второй параметр как 'a
если я объявил это как obj
?
3 ответа
Я полагаю, что это может быть "ожидаемым" поведением (хотя в данном случае и неудачным) в результате процессов обобщения и конденсации компилятора. Рассмотрим пример Томаса:
let foo (s:string) (a:obj) = a
Если бы вы должны были определить
let bar a = foo "test" a
тогда компилятор выведет тип bar : 'a -> obj
потому что это обобщает тип первого аргумента. В вашем случае у вас есть эквивалент
let bar = foo "test"
так bar
это значение, а не синтаксическая функция. Компилятор выполняет по существу ту же процедуру вывода, за исключением того, что теперь применяется ограничение значения. Это неудачно в вашем случае, так как это означает, что вы должны явно аннотировать makeElem
с аннотацией типа (или сделать ее синтаксической функцией).
Это выглядит как неожиданное поведение для меня. Это можно продемонстрировать с помощью более простой функции:
let foo (s:string) (a:obj) = a
let bar = foo "bar" // Value restriction
Одним из возможных объяснений может быть то, что компилятор F# позволяет вам вызывать функцию, принимающую параметр некоторого типа с аргументом любого подтипа. Итак, вы можете позвонить foo "hi" (new A())
без явного приведения A
в obj
(что раньше требовалось).
Это неявное приведение может означать, что компилятор фактически интерпретирует bar
как то так:
let bar a = foo "bar" (a :> obj)
... и поэтому он считает, что аргумент является общим. В любом случае, это всего лишь предположение, поэтому вы можете попробовать отправить это в виде отчета об ошибке в fsbugs на microsoft dot com.
(Следующее основано исключительно на наблюдении.)
Если у вас есть функция obj -> 'a
вызовы этой функции не используются для определения / определения типа ее аргумента. Иллюстрация:
let writeLine (arg: obj) = System.Console.WriteLine(arg)
writeLine
является obj -> unit
let square x =
writeLine x
x * x
В вышеуказанной функции x
выводится как int
потому что (*)
, Если тип может быть ограничен obj
тогда эта функция не будет работать (x
будет выводиться как obj
до использования (*)
, что привело бы к ошибке по типу: type obj
не поддерживает оператор (*)
).
Я думаю, что это хорошее дело. Там нет необходимости ограничивать тип как obj
потому что каждый тип уже неявно преобразуется в obj
, Это позволяет вашей программе быть более универсальной и обеспечивает лучшую совместимость с.NET BCL.
Короче, obj
не имеет никакого отношения к выводу типа (ууу!).