Ограничение значения при отсутствии общих параметров - PullRequest
3 голосов
/ 23 февраля 2012

Я получаю ошибку ограничения значения на 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 ]

1 голос
/ 23 февраля 2012

I полагают , что это может быть "ожидаемым" поведением (хотя в данном случае и неудачным) в результате процессов обобщения и конденсации компилятора.Рассмотрим пример Томаса:

let foo (s:string) (a:obj) = a

Если вы определили

let bar a = foo "test" a

, то компилятор выведет тип bar : 'a -> obj, поскольку он обобщает тип первого аргумента.В вашем случае у вас есть эквивалент

let bar = foo "test"

, поэтому bar является значением, а не синтаксической функцией.Компилятор выполняет по существу ту же процедуру вывода, за исключением того, что теперь применяется ограничение значения.Это прискорбно в вашем случае, так как это означает, что вы должны явно аннотировать makeElem аннотацией типа (или делать ее синтаксической функцией).

0 голосов
/ 23 февраля 2012

(Следующее основано исключительно на наблюдении.)

Если у вас есть функция 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 не имеет отношения к выводу типа (ууу!).

0 голосов
/ 23 февраля 2012

Это выглядит как неожиданное поведение для меня.Это можно продемонстрировать, используя более простую функцию:

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 .

...